From d4d5f66df0d543f643115490f653b68d249038be Mon Sep 17 00:00:00 2001 From: beanbeah <24713371+beanbeah@users.noreply.github.com> Date: Sun, 9 Aug 2026 11:47:46 -0700 Subject: [PATCH] Fix Challenge-8-IDOR-Bank: bind bank balance/transfer to the session's own account DirectObjectBankCurrentBalance and DirectObjectBankTransfer trusted the client-supplied accountNumber / senderAccountNumber outright, never checking it against the bank account the session actually authenticated into (ses.getAttribute("directObjectBankAccount"), set at login). Any signed-in player could read another account's balance, or drain funds out of it (e.g. the seeded high-balance 'Mr. Banks' account) by simply naming it as the sender in a transfer request. Both servlets now resolve the session's own bound account and refuse the request with the existing error.shouldNotBeHere message whenever the supplied account/sender number does not match it. receiverAccountNumber in Transfer is left unrestricted since sending funds to any other account is the legitimate, intended feature. Verified locally (WSL, mvn -Pdocker + docker compose, MariaDB/Mongo/ Tomcat): before the fix, POSTing senderAccountNumber= with my own account as receiver successfully drained the account and unlocked the challenge result key; after the fix the same request is refused and no funds move, while refreshing/transferring from my own account still works exactly as before. Co-Authored-By: Claude Sonnet 5 --- .../DirectObjectBankCurrentBalance.java | 12 ++++++++++++ .../challenge/DirectObjectBankTransfer.java | 17 +++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/main/java/servlets/module/challenge/DirectObjectBankCurrentBalance.java b/src/main/java/servlets/module/challenge/DirectObjectBankCurrentBalance.java index eb4438da4..c88130d8a 100644 --- a/src/main/java/servlets/module/challenge/DirectObjectBankCurrentBalance.java +++ b/src/main/java/servlets/module/challenge/DirectObjectBankCurrentBalance.java @@ -69,6 +69,18 @@ public void doPost(HttpServletRequest request, HttpServletResponse response) try { String accountNumber = request.getParameter("accountNumber"); log.debug("Account Number - " + accountNumber); + Object boundAccount = ses.getAttribute("directObjectBankAccount"); + if (boundAccount == null || !boundAccount.toString().equals(accountNumber)) { + log.warn( + levelName + + " - Rejected balance lookup for account " + + accountNumber + + " requested by session bound to " + + boundAccount + + ". This is not their account."); + out.write(errors.getString("error.shouldNotBeHere")); + return; + } String applicationRoot = getServletContext().getRealPath(""); String htmlOutput = new String(); long currentBalance = diff --git a/src/main/java/servlets/module/challenge/DirectObjectBankTransfer.java b/src/main/java/servlets/module/challenge/DirectObjectBankTransfer.java index 2ceaafaed..6483fbde8 100644 --- a/src/main/java/servlets/module/challenge/DirectObjectBankTransfer.java +++ b/src/main/java/servlets/module/challenge/DirectObjectBankTransfer.java @@ -81,9 +81,22 @@ public void doPost(HttpServletRequest request, HttpServletResponse response) log.debug("Transfer Amount - " + transferAmountString); float tranferAmount = Float.parseFloat(transferAmountString); + Object boundAccount = ses.getAttribute("directObjectBankAccount"); + // Data Validation - // Positive Transfer Amount? - if (tranferAmount > 0) { + // Sender Account must be the account the session is actually signed in to. Funds may + // only ever be moved out of the account that was authenticated with, never an + // arbitrary account number supplied by the client. + if (boundAccount == null || !boundAccount.toString().equals(senderAccountNumber)) { + log.warn( + levelName + + " - Rejected transfer attempt out of account " + + senderAccountNumber + + " by session bound to " + + boundAccount + + ". This is not their account."); + errorMessage = errors.getString("error.shouldNotBeHere"); + } else if (tranferAmount > 0) { // Sender Account Has necessary funds? long senderFunds = DirectObjectBankLogin.getAccountBalance(senderAccountNumber, applicationRoot);