Resolv USR Counter SERVICE_ROLE KMS Compromise / Unbacked Mint + ETH Extraction (2026-03-22)
- Name: Resolv USR Counter
SERVICE_ROLEKMS compromise / unbacked mint + ETH extraction - Date: 2026-03-22
- Protocol: Resolv / USR
- Chain: Ethereum
- Category: offchain signer compromise / operator-boundary failure / unbacked stablecoin mint / mint-authorization failure / stale-price downstream contagion
- Loss: Resolv said attackers minted
80M USRagainst only about$200kof USDC deposits and extracted about$25Mas ETH; Nexus Mutual and CertiK put the realized onchain extraction around$23M-$26.8M, with additional downstream lending-market bad-debt and frozen-liquidity effects after stale collateral pricing kept treatingUSRnear par - Summary: Resolv’s permissionless USR mint path split into two steps: anyone could call
requestSwap()on the USR Counter by depositing collateral, then an authorized offchain service holdingSERVICE_ROLEcalledcompleteSwap()to finalize the exact USR amount to mint. On 2026-03-22, attackers chained an upstream credential compromise into GitHub access, used a malicious workflow to exfiltrate cloud credentials, then escalated inside Resolv’s AWS environment until they could change KMS key policy and obtain signing authority over theSERVICE_ROLEEOA0x15CAd41e6BdCaDc7121ce65080489C92CF6de398. With that authority, they created small legitimate swap requests and then finalized them with massively inflatedtargetAmountvalues on USR Counter0xa27a69Ae180e202fDe5D38189a3F24Fe24E55861, minting50M USRin one transaction and30M USRin a second. The contract only enforced thatcompleteSwap()came fromSERVICE_ROLEand that the minted amount met the user’s minimum acceptable output, so once the signer was compromised there was no onchain mint cap, collateral-ratio check, or oracle sanity check to stop80Munbacked USR from being created and dumped through DEX liquidity into ETH. - Trigger / entry point:
- The relevant mint surface was Resolv’s USR Counter contract
0xa27a69Ae180e202fDe5D38189a3F24Fe24E55861, which exposed permissionlessrequestSwap()plus privilegedcompleteSwap()settlement. - Resolv’s postmortem says the attack began outside its perimeter, when a credential associated with a contractor’s GitHub account from a previously compromised third-party project gave attackers an initial foothold.
- Attackers used that foothold to access some Resolv GitHub repositories and deploy a malicious workflow that exfiltrated infrastructure credentials without relying on obvious outbound-network exfiltration.
- With cloud access, they reconnoitered services, failed through multiple escalation attempts, and finally used a higher-privileged role to modify the KMS key’s access policy directly so they could sign as the onchain
SERVICE_ROLEauthority. - Once that happened, the decisive mint boundary was gone: the protocol’s onchain side did not independently cap or price-check the USR amount that
SERVICE_ROLEchose to finalize.
- The relevant mint surface was Resolv’s USR Counter contract
- Root cause:
- The immediate cause was compromise of the offchain signing authority that Resolv used to authorize mint completion, not a classic arithmetic or reentrancy bug in the Counter contract.
- The sharper design failure was that
SERVICE_ROLEpossession effectively equaled mint truth.completeSwap()could finalize an arbitrary hightargetAmountso long as it cleared the request’s minimum-output floor. - Resolv’s own remediation list is revealing: after the incident it specifically planned onchain mint caps and oracle-based mint validation, implying those independent constraints were absent when the compromised signer acted.
- The upstream-to-cloud chain also mattered. Resolv says production branch protections blocked direct malicious code deployment, so the exploit path worked by climbing around the code path and seizing the operational authority that the contracts already trusted.
- Exploit path:
- Attackers obtained an initial foothold through a contractor-linked GitHub credential exposed by compromise at a third-party project where that contractor had previously contributed.
- They used that foothold to access Resolv GitHub resources and install a malicious workflow that exfiltrated cloud credentials.
- Inside cloud infrastructure, they enumerated services and tried several escalation paths before using a higher-privileged role to edit the KMS key policy and grant themselves signing ability over the service signer.
- At
01:50:59 UTC, the attacker created a real USR swap request on Counter with about100,000 USDCin transaction0x590b5c66df27b7f34cde721ca1b5f973ae047ffda370610491f694dade732c89. - At
02:21:35 UTC, the compromisedSERVICE_ROLEexecutedcompleteSwap()in transaction0xfe37f25efd67d0a4da4afe48509b258df48757b97810b28ce4c649658dc33743, resolving requestid=30with a50M USRtarget amount rather than something near the deposited collateral value. - Over the next roughly 80 minutes, the attackers split activity across multiple wallets and routed the newly minted USR through Curve, KyberSwap, Uniswap, and Velodrome to convert protocol-authored fiction into ETH.
- They repeated the pattern with another swap request (
0xe5bae64ee813c742f06b5d045397c2eefa31532e4d6c21b7c6da129eaa17ae4b) and a second privileged settlement at03:41 UTCin transaction0x41b6b9376d174165cbd54ba576c8f6675ff966f17609a7b80d27d8652db1f18f, minting another30M USR. - By the time Resolv paused contracts and revoked credentials, attackers had extracted about
11,408.85 ETHand left USR badly depegged. - In the aftermath, Resolv burned or blacklisted roughly
46Mof the illicit supply, while downstream lending markets had to deal with stale-oracle exposure and frozen bad debt.
- What the attacker needed to be true:
requestSwap()had to remain permissionless, so the attacker could create legitimate pending requests with small collateral deposits.completeSwap()had to trust a single operational signer boundary strongly enough that compromising it let the attacker choose the final mint amount.- The contract needed to enforce only a lower bound (
minAmountOut) rather than a protocol-protective upper bound derived from collateral value, oracle pricing, or mint caps. - DEX liquidity had to be deep enough that attacker-minted USR could be sold into ETH before pauses and blacklists fully contained the supply.
- Downstream lending protocols had to keep recognizing stale
USRandwstUSRoracle values, magnifying the blast radius after the primary overmint.
- Technical anatomy worth remembering:
- This is a clean example of
minimum user protection != maximum protocol protection. A request-levelminOutguard prevented underfills for honest users but did nothing to stop privileged over-minting after signer compromise. - The key compromise sat above the nominal onchain role. Resolv says the attackers first failed to access the signer directly, then won by changing key policy so the environment itself would produce valid signatures. That distinction matters: the private key did not need to be exported for the protocol to lose mint integrity.
- Eighteen audits and a bug bounty did not stop this because the core contract behavior was consistent with design. The broken assumption was that offchain authorization would stay trustworthy and therefore did not need independent onchain ceilings.
- The second-order damage path was also important. Nexus Mutual noted that some lending markets were still using a NAV-based oracle that had not updated for about 15 hours, so depegged USR could continue to appear as near-par collateral even after the primary exploit was obvious.
- Resolv’s own planned fixes describe the missing defense-in-depth stack precisely: onchain mint caps, oracle-based price validation, automated emergency pause, stronger CI/CD and cloud-auth boundaries, and tighter GitHub permissions.
- This is a clean example of
- Representative transactions and addresses:
- USR Counter:
0xa27a69Ae180e202fDe5D38189a3F24Fe24E55861 SERVICE_ROLEEOA:0x15CAd41e6BdCaDc7121ce65080489C92CF6de398- First request creation:
0x590b5c66df27b7f34cde721ca1b5f973ae047ffda370610491f694dade732c89 - First illicit
completeSwap():0xfe37f25efd67d0a4da4afe48509b258df48757b97810b28ce4c649658dc33743 - Second request creation:
0xe5bae64ee813c742f06b5d045397c2eefa31532e4d6c21b7c6da129eaa17ae4b - Second illicit
completeSwap():0x41b6b9376d174165cbd54ba576c8f6675ff966f17609a7b80d27d8652db1f18f - Exploiter wallets highlighted by CertiK:
0x04A288a7789DD6Ade935361a4fB1Ec5db513caEd,0x8ED8cF0C1c531C1b20848E78f1CB32fa5B99b81C,0x6Db6006c38468CDc0fD7d1c251018b1B696232Ed,0xb945eC1be1f42777F3AA7D683562800B4CDD3890,0x9FeeEAEc113E6d2DCD5ac997d5358eee41836e5f
- USR Counter:
- Response / mitigation:
- Resolv says backend services were halted first, then all relevant pausable contracts were paused onchain at
05:16 UTC. - Compromised credentials were revoked around
05:30 UTC, cutting off the attackers’ cloud access while they were reportedly still active minutes earlier. - The protocol later neutralized about
46Mof the illicitly minted80M USRthrough direct burns and newly deployed blacklist functionality after timelock delay. - Immediate remediation included credential rotation across CI/CD, cloud, exchange, custodian, auth, and VPN systems, plus banning personal access tokens inside the GitHub organization.
- Planned remediation included OIDC-based CI/CD auth, stricter infrastructure-role design, onchain mint caps, oracle-based mint validation, automated pause triggers tied to monitoring, and broader external security review before restart.
- Resolv says backend services were halted first, then all relevant pausable contracts were paused onchain at
Pattern tags
- authorized-action-origination-compromise
- operator-boundary-opacity
- stale-price-exploitation
- GitHub workflow access, AWS role escalation, KMS policy mutation, and
SERVICE_ROLEsigning together formed one offchain mint-authority chain rather than four separable controls requestSwap()authenticated only user intent whilecompleteSwap()let the service signer publish the protocol’s final liability amount without an onchain upper bound- Resolv’s mint path protected against under-delivery to honest users with
minAmountOut, but not against over-delivery to attackers once the authorized settlement role was hostile - Stale downstream
USRpricing widened the blast radius after the primary overmint by letting counterfeit liabilities retain near-par collateral meaning outside Resolv itself
Three settlement-authority promotions this incident collapsed
-
A tiny real swap request could still exist and reach privileged completion at all
- The attacker only had to post about
100,000 USDCand then later another small real request to create legitimaterequestSwap()objects that the Counter would keep open for settlement. - This is only the reachability fact:
some request shell exists that the privileged finisher may still complete.
- The attacker only had to post about
-
SERVICE_ROLEstill promoted attacker-chosentargetAmountinto canonical USR liability truth- Once the KMS policy path was compromised,
completeSwap()accepted50M USRand then30M USRas the protocol’s final mint answer even though the deposited collateral was nowhere near that value. - The dangerous jump was not only
the service role was real; it wasthe service role's chosen completion amount now counts as money-good issuance truth.
- Once the KMS policy path was compromised,
-
Deeper DEX and downstream collateral paths still honored that truth
- The newly printed
USRcould still be dumped into deep external liquidity for ETH, and stale downstream pricing kept letting some venues treatUSRandwstUSRas near-par collateral after the primary overmint. - The real loss therefore landed not only inside Resolv’s request path, but in every reserve and collateral surface still willing to spend against that authenticated but absurd issuance story.
- The newly printed
Why the exploit worked
- Resolv treated one operational signer chain as mint truth. Once the attackers could speak as
SERVICE_ROLE, the protocol no longer re-asked whether the collateral posted inrequestSwap()justified thetargetAmountfinalized incompleteSwap(). - The healthy-side failure was not only
service role compromised, butauthorized settlement originator also chose the economically decisive field. Resolv’s onchain contract verified caller identity and a user-provided minimum floor, yet left the protocol-protective maximum entirely to the compromised offchain role. - The GitHub-to-cloud-to-KMS escalation path mattered because it shows the real authority boundary lived above the visible Ethereum address. The contracts did not care whether the signer key was exported directly or whether KMS policy had been rewritten to produce the same valid signature on demand.
- The attacker only needed tiny real deposits because the request/complete split let legitimate low-value swap requests become shells for attacker-authored high-value liabilities. That made the exploit cheap to initiate and fast to repeat.
- Loss propagation was amplified by downstream oracle lag. Once counterfeit
USRreached DEXs and lending venues, stale pricing let the protocol’s false liabilities keep masquerading as near-par collateral or liquidity even after Resolv itself had begun incident response.
Detection and audit pivots
- Treat any two-step settlement surface like
requestSwap()/completeSwap()as custody code and ask which step chooses the protocol’s final economic output. If the privileged finisher can still pick arbitrary mint size, redemption amount, or payout value, the real invariant lives offchain. - Audit the whole operational authority chain that can speak as the onchain role: repo workflow control, CI/CD credential paths, cloud-role escalation, KMS policy admins, and any service that can mutate signer permissions. If those layers are not jointly reviewed, the onchain role is being modeled too narrowly.
- Search for user-protective floors without protocol-protective ceilings. A
minOutorminimum acceptable amountcheck often signals the contract is defending against underfill but not against authenticated over-issuance after signer compromise. - Simulate
authorized but absurdsettlements against privileged completion paths: tiny deposits with gianttargetAmount, redemptions far above reserve value, or admin-approved issuance outside expected collateral bands. If the contract still executes, containment is missing where value becomes real. - Monitor for second-order contagion surfaces, not only the originating protocol. Stablecoin overmint incidents need alerting for stale collateral feeds, wrapper marks, AMM liquidity exits, and lending markets that will keep honoring the compromised asset after the first exploit transaction lands.
Failure-prevention checklist
- Treat
requestSwap()/completeSwap()style issuance as one balance-sheet object. The finishing role should not be able to choose an arbitrary final liability amount that the request-creation step and onchain state never independently bound. - Put protocol-protective ceilings underneath authenticated settlement: per-request mint caps, per-epoch issuance budgets, collateral-value sanity checks, oracle-bounded maximum output, and route-local throttles that still hold if the service role is hostile.
- Separate user-protective floors from protocol-protective ceilings in review. A
minAmountOutguard helps honest users, but it should never be mistaken for a solvency control. - Treat CI/CD, GitHub workflow control, cloud IAM, KMS policy admins, and service-role mutation paths as part of the mint-authority boundary, not as infrastructure outside the protocol’s custody model.
- Require the downstream contract or settlement lane to re-bind exact request truth: deposited asset, deposited amount, request id, request owner, target liability, and completion window should remain machine-checkably coherent from request creation through final mint.
- Pre-commit degraded modes for signer compromise: lower mint budgets, no large request completion, automated pause on abnormal issuance, and faster isolation of the one settlement lane that can still speak as the service role.
- Quarantine freshly minted or newly depegged liabilities from downstream collateral use quickly. If wrapper markets, lending venues, or NAV oracles keep honoring the asset at par after primary compromise, the protocol has not really bounded loss.
- Rehearse
authorized but absurdincident response for backend settlement paths: tiny real deposits paired with giant completion values, impossible issuance tempo, repeated micro-request overmints, and cross-venue dumps into deep liquidity.
Audit prompts
- Which exact fields can
SERVICE_ROLEor its equivalent still choose unilaterally at settlement time: amount, price, destination asset, request completion status, fee path, or collateral interpretation? - If the settlement signer or KMS policy path is compromised tonight, what hard onchain ceilings still stop a tiny request from becoming a treasury-scale mint?
- Does the contract enforce only
user accepted at least this much, or does it also enforcethe protocol may not issue more than this muchfrom objective collateral and pricing state? - Who can mutate the cloud or KMS policy that effectively speaks as the onchain service role, and is that authority reviewed as mint custody rather than ordinary infrastructure administration?
- Can responders freeze just the completion lane, just one asset pair, or just abnormal mint sizes quickly enough to matter, or do they only have a full shutdown after liquidity has already been drained?
- What downstream venues continue to treat the asset as near-par after an overmint: AMMs, wrappers, lending markets, NAV oracles, collateral adapters, or bridge routes?
- Does monitoring alert on
valid but impossiblesettlement behavior such as hugetargetAmountrelative to deposited collateral, burst issuance from tiny requests, or sudden completion activity outside operational norms? - Can investigators reconstruct one exact request envelope from repo / workflow / cloud logs through to onchain settlement, or does the decisive mint-authority chain become opaque once the service environment is compromised?
Lessons that should already be standard
Authorized settlement roleis not the same thing assafe settlement logic. If that role can choose the economically decisive field without an independent ceiling, the protocol has pushed solvency offchain.- Minimum-output protection for users is not maximum-liability protection for the protocol.
- KMS-backed signing is not a real containment story if policy admins or adjacent cloud roles can simply redefine who gets to produce valid signatures.
- Stablecoin mint completion is already reserve authority when deep external liquidity and downstream collateral adapters will honor the printed liability as money-good.
- Operational-boundary review has to include the whole action-authoring chain. GitHub workflow access, cloud creds, KMS policy mutation, and service-role signing are often one custody surface pretending to be several.
- Overmint incidents are not contained when the originating protocol pauses. If stale oracles and downstream integrations keep treating the counterfeit liability as near-par collateral, the blast radius is still live.
Audited contractdoes not meansafe protocolwhen the contract was intentionally designed to trust an offchain authority that can mint absurd amounts once compromised.
Why this incident matters
- It preserves a failure mode where the contract does exactly what it was built to do, yet the real mint invariant lived offchain in a signer and cloud-policy chain that the contracts did not independently constrain.
- It is a good counterexample to simplistic
audited contracts == safe protocolreasoning. The protocol failed at the boundary where operational signing authority became direct balance-sheet authority. - It also shows how stablecoin mint failures spill outward: once counterfeit supply reaches AMMs and stale oracle consumers, losses no longer stay confined to the protocol that printed the bad inventory.
- Finally, it is a useful anatomy case for incidents where the attacker does not steal collateral directly from custody. They instead make the protocol self-issue false liabilities, then let secondary markets and integrators provide the real assets.
Internal linkages
- Closest recurring pattern: authorized-action-origination-compromise.
- Adjacent recurring patterns: operator-boundary-opacity, signer-quorum-compromise.
- Adjacent downstream-blast-radius pattern: stale-price-exploitation.
- Positive-design companions: settlement envelope truth as a single security object — useful when the sharper healthy-side question is what exact request, payer, collateral amount, and finalized liability object
SERVICE_ROLEshould have been forced to keep machine-checkably identical fromrequestSwap()throughcompleteSwap(); and post-authentication bounded-loss design — useful when the sharper follow-up is why one compromised authenticated mint-completion lane still had enough per-request and per-window authority to print80M USRand immediately cash much of it out against deep ETH liquidity. - Useful comparison incidents: pnetwork-pgala-proxyadmin-key-exposure-whitehat-uncollateralized-mint-pancakeswap-pool-drain-2022-11-03, multichain-mpc-custody-compromise-unauthorized-bridge-withdrawals-2023-07-06, vow-usdratesetter-usdrate-misconfiguration-vusd-overmint-pool-drain-2024-08-13.
Control surface
-
The decisive control surface was not only the USR Counter contract. It was the whole chain that could speak as
SERVICE_ROLE: contractor credential hygiene, GitHub workflow authority, cloud credentials, KMS policy management, and the contract logic that accepted signer-authoredtargetAmountas mint truth. -
Once that chain was compromised,
80M USRwas not a weird accounting side effect. It was the direct output the protocol was authorized to produce under its own rules. -
The protocol’s missing independent check was simple and brutal: no onchain component re-asked whether the requested collateral justified the minted stablecoin quantity before letting that quantity hit markets.
-
Sources:
-
Last reviewed: 2026-08-01 UTC