0x45ce017f5a295f387eafb596b4bcb1192dd1c302ccb9d097d7fa2cdf3008b1390xdb27d4ff4be1cd04c34a7cb6f47402c37cb73459EthereumIn tx 0x45ce017f5a295f387eafb596b4bcb1192dd1c302ccb9d097d7fa2cdf3008b139 on Ethereum block 20729672, the Inumi token contract at 0xdb27d4ff4be1cd04c34a7cb6f47402c37cb73459 was drained of its entire 2 ETH balance. The exploit was permissionless: an attacker-controlled helper contract rewrote the token's marketing-wallet pointer and then called rescueEth() from that new wallet.
The root cause was not a complex multi-party condition. The victim exposed a public setMarketingWallet(address) path and rescueEth() trusted the mutable marketing-wallet value as its sole authorization check. Any unprivileged actor could therefore retarget the recovery recipient to an attacker-controlled payable contract and immediately withdraw the contract's ETH.
The relevant victim is the deployed Inumi token contract 0xdb27d4ff4be1cd04c34a7cb6f47402c37cb73459. The exploitable state existed immediately after block 20729671, when the contract held 2 ETH and storage slot 8 still contained the original marketing wallet 0xd129d8c12f0e7aa51157d9e6cc3f7ece2dc84ecd.
Independent historical RPC checks confirm that pre-state:
cast balance --block 20729671 0xdb27d4ff4be1cd04c34a7cb6f47402c37cb73459
=> 2000000000000000000
cast storage --block 20729671 0xdb27d4ff4be1cd04c34a7cb6f47402c37cb73459 8
=> 0x000000000000000000000000d129d8c12f0e7aa51157d9e6cc3f7ece2dc84ecd
The exploit depended on two externally callable victim selectors:
setMarketingWallet(address) -> 0x5d098b38
rescueEth() -> 0xce31a06b
The attacker used helper contract 0x5b5a0580bcfd3673820bb249514234afad33e209, but that address was not special. The ACT property comes from the fact that any attacker can deploy a fresh payable contract and use it as the replacement wallet.
The vulnerability class is an access-control failure on privileged configuration. The contract allowed arbitrary callers to update the marketing wallet, even though that wallet also functioned as the authorization target for ETH rescue. That coupling turned a mutable configuration field into a privilege-escalation primitive.
The violated invariant is straightforward: only the intended operator should be able to change the ETH rescue recipient, and only that intended recipient should be able to execute recovery. The concrete breakpoint is the unprotected setMarketingWallet(address) write to storage slot 8. Once the attacker sets slot 8 to an attacker-controlled payable contract, rescueEth() accepts the attacker as authorized and transfers the victim's ETH balance. Because the pre-state and required calls are publicly observable and need no privileged secrets, the exploit is deterministic and permissionless.
The stealable state was created one block earlier when tx 0xaa293b2ce135e0ccef84e6cf0a85dfa9b124e0a0cc2321849eff452297127fbb transferred 2 ETH into the token contract. That ETH remained stranded inside the victim at block 20729671.
An independent permissiveness check from an arbitrary EOA succeeds against the victim setter:
cast call --block 20729671 \
--from 0x1111111111111111111111111111111111111111 \
0xdb27d4ff4be1cd04c34a7cb6f47402c37cb73459 \
"setMarketingWallet(address)" \
0x2222222222222222222222222222222222222222
=> 0x
That successful eth_call shows the setter was not protected by ownership or role gating. The exploit transaction then used the attacker helper as the direct caller into the victim. The transaction input embeds the two critical victim calls in sequence:
0x5d098b38 0000000000000000000000005b5a0580bcfd3673820bb249514234afad33e209
0xce31a06b
Those correspond to:
victim.setMarketingWallet(0x5b5a0580bcfd3673820bb249514234afad33e209);
victim.rescueEth();
Post-state checks confirm the privilege handoff and drain:
cast storage --block 20729672 0xdb27d4ff4be1cd04c34a7cb6f47402c37cb73459 8
=> 0x0000000000000000000000005b5a0580bcfd3673820bb249514234afad33e209
cast balance --block 20729672 0xdb27d4ff4be1cd04c34a7cb6f47402c37cb73459
=> 0
The exploit receipt shows status = 1, gasUsed = 69903, and a victim-emitted log whose topics include the new wallet 0x5b5a0580bcfd3673820bb249514234afad33e209. The collected balance diff shows the victim lost exactly 2000000000000000000 wei while the attacker EOA gained 1599386168524108624 wei net after gas and builder payment.
The adversary cluster contains EOA 0x43debe92a7a32dca999593fad617dbd2e6b080a5 and helper contract 0x5b5a0580bcfd3673820bb249514234afad33e209. The helper was deployed earlier in tx 0xeb3ff4f366325c3d1c549bc102d4017282937f7b7cf8c16dd1cd57944eb7ae49, but the exploit does not rely on that exact helper address or bytecode.
The on-chain execution flow is:
// attacker-controlled helper
victim.setMarketingWallet(address(this));
victim.rescueEth();
payable(attackerEOA).transfer(proceedsMinusBuilderPayment);
Operationally, the attacker waited until the victim visibly held ETH, submitted a single transaction to the helper, had the helper rewrite slot 8 to itself, invoked rescueEth() while now authorized, received the full 2 ETH, then distributed proceeds. The trace and balance diff show that 0.399844163861070196 ETH went to 0x388c818ca8b9251b393131c08a736a67ccb19297 and 1.600155836139037101 ETH went to the attacker EOA.
The measurable protocol-side loss was the victim contract's full ETH balance:
[
{
"token_symbol": "ETH",
"amount": "2000000000000000000",
"decimal": 18
}
]
The attacker realized a net profit of 1.599386168524108624 ETH after gas. The exploit required no privileged credentials, no private orderflow, and no attacker-specific on-chain artifact beyond a standard payable helper contract. Any searcher observing the victim's ETH balance and public setter could have executed the same sequence.
0x45ce017f5a295f387eafb596b4bcb1192dd1c302ccb9d097d7fa2cdf3008b1390xaa293b2ce135e0ccef84e6cf0a85dfa9b124e0a0cc2321849eff452297127fbb0xeb3ff4f366325c3d1c549bc102d4017282937f7b7cf8c16dd1cd57944eb7ae490xdb27d4ff4be1cd04c34a7cb6f47402c37cb734590x43debe92a7a32dca999593fad617dbd2e6b080a50x5b5a0580bcfd3673820bb249514234afad33e209eth_call against setMarketingWallet(address)