Resolv USR Counter SERVICE_ROLE KMS Compromise / Unbacked Mint + ETH Extraction (2026-03-22)

  • Name: Resolv USR Counter SERVICE_ROLE KMS 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 USR against only about $200k of USDC deposits and extracted about $25M as 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 treating USR near 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 holding SERVICE_ROLE called completeSwap() 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 the SERVICE_ROLE EOA 0x15CAd41e6BdCaDc7121ce65080489C92CF6de398. With that authority, they created small legitimate swap requests and then finalized them with massively inflated targetAmount values on USR Counter 0xa27a69Ae180e202fDe5D38189a3F24Fe24E55861, minting 50M USR in one transaction and 30M USR in a second. The contract only enforced that completeSwap() came from SERVICE_ROLE and 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 stop 80M unbacked 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 permissionless requestSwap() plus privileged completeSwap() 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_ROLE authority.
    • 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_ROLE chose to finalize.
  • 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_ROLE possession effectively equaled mint truth. completeSwap() could finalize an arbitrary high targetAmount so 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:
    1. 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.
    2. They used that foothold to access Resolv GitHub resources and install a malicious workflow that exfiltrated cloud credentials.
    3. 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.
    4. At 01:50:59 UTC, the attacker created a real USR swap request on Counter with about 100,000 USDC in transaction 0x590b5c66df27b7f34cde721ca1b5f973ae047ffda370610491f694dade732c89.
    5. At 02:21:35 UTC, the compromised SERVICE_ROLE executed completeSwap() in transaction 0xfe37f25efd67d0a4da4afe48509b258df48757b97810b28ce4c649658dc33743, resolving request id=30 with a 50M USR target amount rather than something near the deposited collateral value.
    6. 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.
    7. They repeated the pattern with another swap request (0xe5bae64ee813c742f06b5d045397c2eefa31532e4d6c21b7c6da129eaa17ae4b) and a second privileged settlement at 03:41 UTC in transaction 0x41b6b9376d174165cbd54ba576c8f6675ff966f17609a7b80d27d8652db1f18f, minting another 30M USR.
    8. By the time Resolv paused contracts and revoked credentials, attackers had extracted about 11,408.85 ETH and left USR badly depegged.
    9. In the aftermath, Resolv burned or blacklisted roughly 46M of 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 USR and wstUSR oracle 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-level minOut guard 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.
  • Representative transactions and addresses:
    • USR Counter: 0xa27a69Ae180e202fDe5D38189a3F24Fe24E55861
    • SERVICE_ROLE EOA: 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
  • 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 46M of the illicitly minted 80M USR through 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.

Pattern tags

  • authorized-action-origination-compromise
  • operator-boundary-opacity
  • stale-price-exploitation
  • GitHub workflow access, AWS role escalation, KMS policy mutation, and SERVICE_ROLE signing together formed one offchain mint-authority chain rather than four separable controls
  • requestSwap() authenticated only user intent while completeSwap() 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 USR pricing 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

  1. A tiny real swap request could still exist and reach privileged completion at all

    • The attacker only had to post about 100,000 USDC and then later another small real request to create legitimate requestSwap() 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.
  2. SERVICE_ROLE still promoted attacker-chosen targetAmount into canonical USR liability truth

    • Once the KMS policy path was compromised, completeSwap() accepted 50M USR and then 30M USR as 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 was the service role's chosen completion amount now counts as money-good issuance truth.
  3. Deeper DEX and downstream collateral paths still honored that truth

    • The newly printed USR could still be dumped into deep external liquidity for ETH, and stale downstream pricing kept letting some venues treat USR and wstUSR as 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.

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 in requestSwap() justified the targetAmount finalized in completeSwap().
  • The healthy-side failure was not only service role compromised, but authorized 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 USR reached 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 minOut or minimum acceptable amount check often signals the contract is defending against underfill but not against authenticated over-issuance after signer compromise.
  • Simulate authorized but absurd settlements against privileged completion paths: tiny deposits with giant targetAmount, 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 minAmountOut guard 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 absurd incident 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_ROLE or 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 enforce the protocol may not issue more than this much from 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 impossible settlement behavior such as huge targetAmount relative 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 role is not the same thing as safe 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 contract does not mean safe protocol when 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 protocol reasoning. 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

Control surface