From 7530bc35aa9f5b18ee98b8f92e3fd77a7165f08e Mon Sep 17 00:00:00 2001 From: beanbeah <24713371+beanbeah@users.noreply.github.com> Date: Sun, 9 Aug 2026 11:18:08 -0700 Subject: [PATCH 1/2] fix: close insecure client-side coupon crypto oracle in BrokenCrypto4 The shop page for Insecure Crypto Storage Challenge 4 shipped a hard-coded DES key and IV, along with the ciphertext of every valid coupon code (including a non-public 100%-off code), to the browser via couponCheck.js. Because the key travels with the ciphertext, this gave the coupon codes no real confidentiality: anyone could decrypt the full list client-side, without ever contacting the server, and recover the secret coupon. - Remove the couponCheck.js include and its change-handler from the JSP; the server already independently validates coupon codes against the database in BrokenCrypto4#doPost, so this was a purely cosmetic green/red hint with no legitimate purpose. - Strip the hard-coded key/IV/ciphertexts out of couponCheck.js itself so the secret material is no longer shipped as a static asset either. - Fix two related correctness bugs in the cost math while in the area: the discount was computed as cost * (perCentOff / 100), which integer-divides perCentOff first and truncates any coupon under 100% off to no discount at all; and finalCost summed bananaAmount instead of bananaCost. Verified locally: mvn compile and spotless:check pass; a standalone harness confirms HalfOffOranges (50%) and PleaseTakeAnOrange (10%) now discount correctly, the legitimate 100% coupon still zeroes cost when supplied through the real checkout flow, and grepping the shipped JS/JSP output confirms the DES key/IV/ciphertext list is no longer present. Co-Authored-By: Claude Sonnet 5 --- .../module/challenge/BrokenCrypto4.java | 14 ++++++++----- ...0cc18533a88b2363054a1f391fe855954d12f9.jsp | 21 +++++++++---------- .../couponCheck.js | 6 +++++- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/main/java/servlets/module/challenge/BrokenCrypto4.java b/src/main/java/servlets/module/challenge/BrokenCrypto4.java index 017031089..8625c9704 100644 --- a/src/main/java/servlets/module/challenge/BrokenCrypto4.java +++ b/src/main/java/servlets/module/challenge/BrokenCrypto4.java @@ -128,11 +128,15 @@ public void doPost(HttpServletRequest request, HttpServletResponse response) conn.close(); // Work Out Final Cost - pineappleCost = pineappleCost - (pineappleCost * (perCentOffPineapple / 100)); - appleCost = appleCost - (appleCost * (perCentOffApple / 100)); - bananaCost = bananaCost - (bananaCost * (perCentOffBanana / 100)); - orangeCost = orangeCost - (orangeCost * (perCentOffOrange / 100)); - int finalCost = pineappleCost + appleCost + bananaAmount + orangeCost; + // NOTE: discount must be applied as (cost * perCentOff) / 100, not + // cost * (perCentOff / 100) - the latter performs integer division on + // perCentOff first, truncating any coupon under 100% off to a 0% + // discount. + pineappleCost = pineappleCost - ((pineappleCost * perCentOffPineapple) / 100); + appleCost = appleCost - ((appleCost * perCentOffApple) / 100); + bananaCost = bananaCost - ((bananaCost * perCentOffBanana) / 100); + orangeCost = orangeCost - ((orangeCost * perCentOffOrange) / 100); + int finalCost = pineappleCost + appleCost + bananaCost + orangeCost; // Output Order htmlOutput = diff --git a/src/main/webapp/challenges/b927fc4d8c9f70a78f8b6fc46a0cc18533a88b2363054a1f391fe855954d12f9.jsp b/src/main/webapp/challenges/b927fc4d8c9f70a78f8b6fc46a0cc18533a88b2363054a1f391fe855954d12f9.jsp index 92507c3e5..cf227430d 100644 --- a/src/main/webapp/challenges/b927fc4d8c9f70a78f8b6fc46a0cc18533a88b2363054a1f391fe855954d12f9.jsp +++ b/src/main/webapp/challenges/b927fc4d8c9f70a78f8b6fc46a0cc18533a88b2363054a1f391fe855954d12f9.jsp @@ -180,17 +180,16 @@ if (request.getSession() != null) }); }); - - + <%-- + The previous client-side "is this coupon valid" hint loaded couponCheck.js, + which shipped a hard-coded symmetric key/IV plus the ciphertext of every + valid coupon code (including a non-public one) straight to the browser. + Since the key travels with the ciphertext, that "encryption" gives the + coupon codes no real confidentiality - anyone can decrypt them client-side + without ever contacting the server. Coupon validity is already correctly + enforced server-side in the doPost handler, so this purely cosmetic + green/red hint is removed rather than reworked. + --%> <% if(Analytics.googleAnalyticsOn) { %><%= Analytics.googleAnalyticsScript %> <% } %> <% diff --git a/src/main/webapp/challenges/b927fc4d8c9f70a78f8b6fc46a0cc18533a88b2363054a1f391fe855954d12f9/couponCheck.js b/src/main/webapp/challenges/b927fc4d8c9f70a78f8b6fc46a0cc18533a88b2363054a1f391fe855954d12f9/couponCheck.js index fa928c14e..09d112af0 100644 --- a/src/main/webapp/challenges/b927fc4d8c9f70a78f8b6fc46a0cc18533a88b2363054a1f391fe855954d12f9/couponCheck.js +++ b/src/main/webapp/challenges/b927fc4d8c9f70a78f8b6fc46a0cc18533a88b2363054a1f391fe855954d12f9/couponCheck.js @@ -1 +1,5 @@ -var _0x34ea=["\x6C\x65\x6E\x67\x74\x68","\x20\x20\x20\x20\x20\x20\x20\x20","\x66\x72\x6F\x6D\x43\x68\x61\x72\x43\x6F\x64\x65","\x00\x00\x00\x00\x00\x00\x00\x00","","\x63\x68\x61\x72\x43\x6F\x64\x65\x41\x74","\x72\x65\x70\x6C\x61\x63\x65","\x30\x34\x38\x63\x63\x64\x31\x66\x62\x36\x30\x36\x37\x65\x65\x30\x65\x33\x30\x34\x64\x63\x32\x30\x32\x35\x62\x39\x36\x66\x34\x62","\x32\x39\x36\x62\x36\x36\x66\x33\x65\x33\x33\x32\x61\x62\x34\x63\x32\x37\x35\x30\x31\x63\x61\x31\x37\x35\x63\x33\x62\x33\x34\x64","\x30\x34\x38\x63\x63\x64\x31\x66\x62\x36\x30\x36\x37\x65\x65\x30\x64\x34\x63\x61\x35\x64\x61\x37\x65\x38\x39\x39\x64\x65\x66\x36","\x62\x61\x64\x35\x65\x64\x65\x31\x38\x38\x62\x64\x31\x64\x66\x31\x64\x62\x38\x62\x30\x36\x30\x33\x31\x38\x36\x37\x36\x32\x31\x61","\x30\x34\x38\x63\x63\x64\x31\x66\x62\x36\x30\x36\x37\x65\x65\x30\x64\x36\x62\x64\x39\x38\x62\x64\x62\x39\x61\x31\x61\x39\x62\x38\x61\x33\x66\x30\x39\x62\x37\x35\x31\x30\x37\x63\x61\x30\x64\x61","\x66\x33\x31\x36\x65\x33\x31\x66\x34\x35\x38\x31\x31\x63\x37\x32\x63\x34\x65\x30\x34\x33\x38\x30\x65\x61\x63\x34\x34\x65\x31\x33","\x34\x37\x65\x30\x34\x36\x66\x62\x32\x35\x30\x61\x30\x62\x39\x35\x63\x61\x64\x65\x33\x65\x66\x66\x34\x65\x62\x64\x61\x32\x39\x63\x39\x33\x31\x35\x37\x62\x32\x66\x63\x30\x31\x63\x32\x34\x33\x30","\x73\x70\x6C\x69\x74","\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x61\x62\x63\x64\x65\x66","\x0A","\x30\x62\x61\x39\x35\x30\x64\x30\x38\x38\x33\x30\x63\x38\x30\x37\x39\x62\x64\x65\x64\x37\x31\x62\x38\x35\x32\x39\x33\x34\x34\x35\x33\x64\x62\x38\x66\x34\x66\x66\x66\x66\x31\x66\x35\x38\x34\x32","\x38\x32\x31\x66\x64\x33\x38\x62\x39\x61\x37\x63\x30\x32\x34\x37","\x69\x6E\x64\x65\x78\x4F\x66"];function des(_0x655fx2,_0x655fx3,_0x655fx4,_0x655fx5,_0x655fx6,_0x655fx7){var _0x655fx8= new Array(0x1010400,0,0x10000,0x1010404,0x1010004,0x10404,0x4,0x10000,0x400,0x1010400,0x1010404,0x400,0x1000404,0x1010004,0x1000000,0x4,0x404,0x1000400,0x1000400,0x10400,0x10400,0x1010000,0x1010000,0x1000404,0x10004,0x1000004,0x1000004,0x10004,0,0x404,0x10404,0x1000000,0x10000,0x1010404,0x4,0x1010000,0x1010400,0x1000000,0x1000000,0x400,0x1010004,0x10000,0x10400,0x1000004,0x400,0x4,0x1000404,0x10404,0x1010404,0x10004,0x1010000,0x1000404,0x1000004,0x404,0x10404,0x1010400,0x404,0x1000400,0x1000400,0,0x10004,0x10400,0,0x1010004);var _0x655fx9= new Array(-0x7fef7fe0,-0x7fff8000,0x8000,0x108020,0x100000,0x20,-0x7fefffe0,-0x7fff7fe0,-0x7fffffe0,-0x7fef7fe0,-0x7fef8000,-0x80000000,-0x7fff8000,0x100000,0x20,-0x7fefffe0,0x108000,0x100020,-0x7fff7fe0,0,-0x80000000,0x8000,0x108020,-0x7ff00000,0x100020,-0x7fffffe0,0,0x108000,0x8020,-0x7fef8000,-0x7ff00000,0x8020,0,0x108020,-0x7fefffe0,0x100000,-0x7fff7fe0,-0x7ff00000,-0x7fef8000,0x8000,-0x7ff00000,-0x7fff8000,0x20,-0x7fef7fe0,0x108020,0x20,0x8000,-0x80000000,0x8020,-0x7fef8000,0x100000,-0x7fffffe0,0x100020,-0x7fff7fe0,-0x7fffffe0,0x100020,0x108000,0,-0x7fff8000,0x8020,-0x80000000,-0x7fefffe0,-0x7fef7fe0,0x108000);var _0x655fxa= new Array(0x208,0x8020200,0,0x8020008,0x8000200,0,0x20208,0x8000200,0x20008,0x8000008,0x8000008,0x20000,0x8020208,0x20008,0x8020000,0x208,0x8000000,0x8,0x8020200,0x200,0x20200,0x8020000,0x8020008,0x20208,0x8000208,0x20200,0x20000,0x8000208,0x8,0x8020208,0x200,0x8000000,0x8020200,0x8000000,0x20008,0x208,0x20000,0x8020200,0x8000200,0,0x200,0x20008,0x8020208,0x8000200,0x8000008,0x200,0,0x8020008,0x8000208,0x20000,0x8000000,0x8020208,0x8,0x20208,0x20200,0x8000008,0x8020000,0x8000208,0x208,0x8020000,0x20208,0x8,0x8020008,0x20200);var _0x655fxb= new Array(0x802001,0x2081,0x2081,0x80,0x802080,0x800081,0x800001,0x2001,0,0x802000,0x802000,0x802081,0x81,0,0x800080,0x800001,0x1,0x2000,0x800000,0x802001,0x80,0x800000,0x2001,0x2080,0x800081,0x1,0x2080,0x800080,0x2000,0x802080,0x802081,0x81,0x800080,0x800001,0x802000,0x802081,0x81,0,0,0x802000,0x2080,0x800080,0x800081,0x1,0x802001,0x2081,0x2081,0x80,0x802081,0x81,0x1,0x2000,0x800001,0x2001,0x802080,0x800081,0x2001,0x2080,0x800000,0x802001,0x80,0x800000,0x2000,0x802080);var _0x655fxc= new Array(0x100,0x2080100,0x2080000,0x42000100,0x80000,0x100,0x40000000,0x2080000,0x40080100,0x80000,0x2000100,0x40080100,0x42000100,0x42080000,0x80100,0x40000000,0x2000000,0x40080000,0x40080000,0,0x40000100,0x42080100,0x42080100,0x2000100,0x42080000,0x40000100,0,0x42000000,0x2080100,0x2000000,0x42000000,0x80100,0x80000,0x42000100,0x100,0x2000000,0x40000000,0x2080000,0x42000100,0x40080100,0x2000100,0x40000000,0x42080000,0x2080100,0x40080100,0x100,0x2000000,0x42080000,0x42080100,0x80100,0x42000000,0x42080100,0x2080000,0,0x40080000,0x42000000,0x80100,0x2000100,0x40000100,0x80000,0,0x40080000,0x2080100,0x40000100);var _0x655fxd= new Array(0x20000010,0x20400000,0x4000,0x20404010,0x20400000,0x10,0x20404010,0x400000,0x20004000,0x404010,0x400000,0x20000010,0x400010,0x20004000,0x20000000,0x4010,0,0x400010,0x20004010,0x4000,0x404000,0x20004010,0x10,0x20400010,0x20400010,0,0x404010,0x20404000,0x4010,0x404000,0x20404000,0x20000000,0x20004000,0x10,0x20400010,0x404000,0x20404010,0x400000,0x4010,0x20000010,0x400000,0x20004000,0x20000000,0x4010,0x20000010,0x20404010,0x404000,0x20400000,0x404010,0x20404000,0,0x20400010,0x10,0x4000,0x20400000,0x404010,0x4000,0x400010,0x20004010,0,0x20404000,0x20000000,0x400010,0x20004010);var _0x655fxe= new Array(0x200000,0x4200002,0x4000802,0,0x800,0x4000802,0x200802,0x4200800,0x4200802,0x200000,0,0x4000002,0x2,0x4000000,0x4200002,0x802,0x4000800,0x200802,0x200002,0x4000800,0x4000002,0x4200000,0x4200800,0x200002,0x4200000,0x800,0x802,0x4200802,0x200800,0x2,0x4000000,0x200800,0x4000000,0x200800,0x200000,0x4000802,0x4000802,0x4200002,0x4200002,0x2,0x200002,0x4000000,0x4000800,0x200000,0x4200800,0x802,0x200802,0x4200800,0x802,0x4000002,0x4200802,0x4200000,0x200800,0,0x2,0x4200802,0,0x200802,0x4200000,0x800,0x4000002,0x4000800,0x800,0x200002);var _0x655fxf= new Array(0x10001040,0x1000,0x40000,0x10041040,0x10000000,0x10001040,0x40,0x10000000,0x40040,0x10040000,0x10041040,0x41000,0x10041000,0x41040,0x1000,0x40,0x10040000,0x10000040,0x10001000,0x1040,0x41000,0x40040,0x10040040,0x10041000,0x1040,0,0,0x10040040,0x10000040,0x10001000,0x41040,0x40000,0x41040,0x40000,0x10041000,0x1000,0x40,0x10040040,0x1000,0x41040,0x10001000,0x40,0x10000040,0x10040000,0x10040040,0x10000000,0x40000,0x10001040,0,0x10041040,0x40040,0x10000040,0x10040000,0x10001000,0x10001040,0,0x10041040,0x41000,0x41000,0x1040,0x1040,0x40040,0x10000000,0x10041000);var _0x655fx10=des_createKeys(_0x655fx2);var _0x655fx11=0,_0x655fx12,_0x655fx13,_0x655fx14,_0x655fx15,_0x655fx16,_0x655fx17,_0x655fx18,_0x655fx19,_0x655fx1a;var _0x655fx1b,_0x655fx1c,_0x655fx1d,_0x655fx1e;var _0x655fx1f,_0x655fx20;var _0x655fx21=_0x655fx3[_0x34ea[0]];var _0x655fx22=0;var _0x655fx23=_0x655fx10[_0x34ea[0]]==32?3:9;if(_0x655fx23==3){_0x655fx1a=_0x655fx4? new Array(0,32,2): new Array(30,-2,-2)}else {_0x655fx1a=_0x655fx4? new Array(0,32,2,62,30,-2,64,96,2): new Array(94,62,-2,32,64,2,30,-2,-2)};if(_0x655fx7==2){_0x655fx3+=_0x34ea[1]}else {if(_0x655fx7==1){_0x655fx14=8-(_0x655fx21%8);_0x655fx3+=String[_0x34ea[2]](_0x655fx14,_0x655fx14,_0x655fx14,_0x655fx14,_0x655fx14,_0x655fx14,_0x655fx14,_0x655fx14);if(_0x655fx14==8){_0x655fx21+=8};}else {if(!_0x655fx7){_0x655fx3+=_0x34ea[3]}}};result=_0x34ea[4];tempresult=_0x34ea[4];if(_0x655fx5==1){_0x655fx1b=(_0x655fx6[_0x34ea[5]](_0x655fx11++)<<24)|(_0x655fx6[_0x34ea[5]](_0x655fx11++)<<16)|(_0x655fx6[_0x34ea[5]](_0x655fx11++)<<8)|_0x655fx6[_0x34ea[5]](_0x655fx11++);_0x655fx1d=(_0x655fx6[_0x34ea[5]](_0x655fx11++)<<24)|(_0x655fx6[_0x34ea[5]](_0x655fx11++)<<16)|(_0x655fx6[_0x34ea[5]](_0x655fx11++)<<8)|_0x655fx6[_0x34ea[5]](_0x655fx11++);_0x655fx11=0;};while(_0x655fx11<_0x655fx21){_0x655fx18=(_0x655fx3[_0x34ea[5]](_0x655fx11++)<<24)|(_0x655fx3[_0x34ea[5]](_0x655fx11++)<<16)|(_0x655fx3[_0x34ea[5]](_0x655fx11++)<<8)|_0x655fx3[_0x34ea[5]](_0x655fx11++);_0x655fx19=(_0x655fx3[_0x34ea[5]](_0x655fx11++)<<24)|(_0x655fx3[_0x34ea[5]](_0x655fx11++)<<16)|(_0x655fx3[_0x34ea[5]](_0x655fx11++)<<8)|_0x655fx3[_0x34ea[5]](_0x655fx11++);if(_0x655fx5==1){if(_0x655fx4){_0x655fx18^=_0x655fx1b;_0x655fx19^=_0x655fx1d;}else {_0x655fx1c=_0x655fx1b;_0x655fx1e=_0x655fx1d;_0x655fx1b=_0x655fx18;_0x655fx1d=_0x655fx19;}};_0x655fx14=((_0x655fx18>>>4)^_0x655fx19)&0x0f0f0f0f;_0x655fx19^=_0x655fx14;_0x655fx18^=(_0x655fx14<<4);_0x655fx14=((_0x655fx18>>>16)^_0x655fx19)&0x0000ffff;_0x655fx19^=_0x655fx14;_0x655fx18^=(_0x655fx14<<16);_0x655fx14=((_0x655fx19>>>2)^_0x655fx18)&0x33333333;_0x655fx18^=_0x655fx14;_0x655fx19^=(_0x655fx14<<2);_0x655fx14=((_0x655fx19>>>8)^_0x655fx18)&0x00ff00ff;_0x655fx18^=_0x655fx14;_0x655fx19^=(_0x655fx14<<8);_0x655fx14=((_0x655fx18>>>1)^_0x655fx19)&0x55555555;_0x655fx19^=_0x655fx14;_0x655fx18^=(_0x655fx14<<1);_0x655fx18=((_0x655fx18<<1)|(_0x655fx18>>>31));_0x655fx19=((_0x655fx19<<1)|(_0x655fx19>>>31));for(_0x655fx13=0;_0x655fx13<_0x655fx23;_0x655fx13+=3){_0x655fx1f=_0x655fx1a[_0x655fx13+1];_0x655fx20=_0x655fx1a[_0x655fx13+2];for(_0x655fx12=_0x655fx1a[_0x655fx13];_0x655fx12!=_0x655fx1f;_0x655fx12+=_0x655fx20){_0x655fx16=_0x655fx19^_0x655fx10[_0x655fx12];_0x655fx17=((_0x655fx19>>>4)|(_0x655fx19<<28))^_0x655fx10[_0x655fx12+1];_0x655fx14=_0x655fx18;_0x655fx18=_0x655fx19;_0x655fx19=_0x655fx14^(_0x655fx9[(_0x655fx16>>>24)&0x3f]|_0x655fxb[(_0x655fx16>>>16)&0x3f]|_0x655fxd[(_0x655fx16>>>8)&0x3f]|_0x655fxf[_0x655fx16&0x3f]|_0x655fx8[(_0x655fx17>>>24)&0x3f]|_0x655fxa[(_0x655fx17>>>16)&0x3f]|_0x655fxc[(_0x655fx17>>>8)&0x3f]|_0x655fxe[_0x655fx17&0x3f]);};_0x655fx14=_0x655fx18;_0x655fx18=_0x655fx19;_0x655fx19=_0x655fx14;};_0x655fx18=((_0x655fx18>>>1)|(_0x655fx18<<31));_0x655fx19=((_0x655fx19>>>1)|(_0x655fx19<<31));_0x655fx14=((_0x655fx18>>>1)^_0x655fx19)&0x55555555;_0x655fx19^=_0x655fx14;_0x655fx18^=(_0x655fx14<<1);_0x655fx14=((_0x655fx19>>>8)^_0x655fx18)&0x00ff00ff;_0x655fx18^=_0x655fx14;_0x655fx19^=(_0x655fx14<<8);_0x655fx14=((_0x655fx19>>>2)^_0x655fx18)&0x33333333;_0x655fx18^=_0x655fx14;_0x655fx19^=(_0x655fx14<<2);_0x655fx14=((_0x655fx18>>>16)^_0x655fx19)&0x0000ffff;_0x655fx19^=_0x655fx14;_0x655fx18^=(_0x655fx14<<16);_0x655fx14=((_0x655fx18>>>4)^_0x655fx19)&0x0f0f0f0f;_0x655fx19^=_0x655fx14;_0x655fx18^=(_0x655fx14<<4);if(_0x655fx5==1){if(_0x655fx4){_0x655fx1b=_0x655fx18;_0x655fx1d=_0x655fx19;}else {_0x655fx18^=_0x655fx1c;_0x655fx19^=_0x655fx1e;}};tempresult+=String[_0x34ea[2]]((_0x655fx18>>>24),((_0x655fx18>>>16)&0xff),((_0x655fx18>>>8)&0xff),(_0x655fx18&0xff),(_0x655fx19>>>24),((_0x655fx19>>>16)&0xff),((_0x655fx19>>>8)&0xff),(_0x655fx19&0xff));_0x655fx22+=8;if(_0x655fx22==512){result+=tempresult;tempresult=_0x34ea[4];_0x655fx22=0;};};result+=tempresult;result=result[_0x34ea[6]](/\0*$/g,_0x34ea[4]);return result;}function des_createKeys(_0x655fx2){pc2bytes0= new Array(0,0x4,0x20000000,0x20000004,0x10000,0x10004,0x20010000,0x20010004,0x200,0x204,0x20000200,0x20000204,0x10200,0x10204,0x20010200,0x20010204);pc2bytes1= new Array(0,0x1,0x100000,0x100001,0x4000000,0x4000001,0x4100000,0x4100001,0x100,0x101,0x100100,0x100101,0x4000100,0x4000101,0x4100100,0x4100101);pc2bytes2= new Array(0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808,0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808);pc2bytes3= new Array(0,0x200000,0x8000000,0x8200000,0x2000,0x202000,0x8002000,0x8202000,0x20000,0x220000,0x8020000,0x8220000,0x22000,0x222000,0x8022000,0x8222000);pc2bytes4= new Array(0,0x40000,0x10,0x40010,0,0x40000,0x10,0x40010,0x1000,0x41000,0x1010,0x41010,0x1000,0x41000,0x1010,0x41010);pc2bytes5= new Array(0,0x400,0x20,0x420,0,0x400,0x20,0x420,0x2000000,0x2000400,0x2000020,0x2000420,0x2000000,0x2000400,0x2000020,0x2000420);pc2bytes6= new Array(0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002,0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002);pc2bytes7= new Array(0,0x10000,0x800,0x10800,0x20000000,0x20010000,0x20000800,0x20010800,0x20000,0x30000,0x20800,0x30800,0x20020000,0x20030000,0x20020800,0x20030800);pc2bytes8= new Array(0,0x40000,0,0x40000,0x2,0x40002,0x2,0x40002,0x2000000,0x2040000,0x2000000,0x2040000,0x2000002,0x2040002,0x2000002,0x2040002);pc2bytes9= new Array(0,0x10000000,0x8,0x10000008,0,0x10000000,0x8,0x10000008,0x400,0x10000400,0x408,0x10000408,0x400,0x10000400,0x408,0x10000408);pc2bytes10= new Array(0,0x20,0,0x20,0x100000,0x100020,0x100000,0x100020,0x2000,0x2020,0x2000,0x2020,0x102000,0x102020,0x102000,0x102020);pc2bytes11= new Array(0,0x1000000,0x200,0x1000200,0x200000,0x1200000,0x200200,0x1200200,0x4000000,0x5000000,0x4000200,0x5000200,0x4200000,0x5200000,0x4200200,0x5200200);pc2bytes12= new Array(0,0x1000,0x8000000,0x8001000,0x80000,0x81000,0x8080000,0x8081000,0x10,0x1010,0x8000010,0x8001010,0x80010,0x81010,0x8080010,0x8081010);pc2bytes13= new Array(0,0x4,0x100,0x104,0,0x4,0x100,0x104,0x1,0x5,0x101,0x105,0x1,0x5,0x101,0x105);var _0x655fx23=_0x655fx2[_0x34ea[0]]>8?3:1;var _0x655fx10= new Array(32*_0x655fx23);var _0x655fx25= new Array(0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0);var _0x655fx26,_0x655fx27,_0x655fx11=0,_0x655fx28=0,_0x655fx14;for(var _0x655fx13=0;_0x655fx13<_0x655fx23;_0x655fx13++){left=(_0x655fx2[_0x34ea[5]](_0x655fx11++)<<24)|(_0x655fx2[_0x34ea[5]](_0x655fx11++)<<16)|(_0x655fx2[_0x34ea[5]](_0x655fx11++)<<8)|_0x655fx2[_0x34ea[5]](_0x655fx11++);right=(_0x655fx2[_0x34ea[5]](_0x655fx11++)<<24)|(_0x655fx2[_0x34ea[5]](_0x655fx11++)<<16)|(_0x655fx2[_0x34ea[5]](_0x655fx11++)<<8)|_0x655fx2[_0x34ea[5]](_0x655fx11++);_0x655fx14=((left>>>4)^right)&0x0f0f0f0f;right^=_0x655fx14;left^=(_0x655fx14<<4);_0x655fx14=((right>>> -16)^left)&0x0000ffff;left^=_0x655fx14;right^=(_0x655fx14<< -16);_0x655fx14=((left>>>2)^right)&0x33333333;right^=_0x655fx14;left^=(_0x655fx14<<2);_0x655fx14=((right>>> -16)^left)&0x0000ffff;left^=_0x655fx14;right^=(_0x655fx14<< -16);_0x655fx14=((left>>>1)^right)&0x55555555;right^=_0x655fx14;left^=(_0x655fx14<<1);_0x655fx14=((right>>>8)^left)&0x00ff00ff;left^=_0x655fx14;right^=(_0x655fx14<<8);_0x655fx14=((left>>>1)^right)&0x55555555;right^=_0x655fx14;left^=(_0x655fx14<<1);_0x655fx14=(left<<8)|((right>>>20)&0x000000f0);left=(right<<24)|((right<<8)&0xff0000)|((right>>>8)&0xff00)|((right>>>24)&0xf0);right=_0x655fx14;for(i=0;i<_0x655fx25[_0x34ea[0]];i++){if(_0x655fx25[i]){left=(left<<2)|(left>>>26);right=(right<<2)|(right>>>26);}else {left=(left<<1)|(left>>>27);right=(right<<1)|(right>>>27);};left&= -0xf;right&= -0xf;_0x655fx26=pc2bytes0[left>>>28]|pc2bytes1[(left>>>24)&0xf]|pc2bytes2[(left>>>20)&0xf]|pc2bytes3[(left>>>16)&0xf]|pc2bytes4[(left>>>12)&0xf]|pc2bytes5[(left>>>8)&0xf]|pc2bytes6[(left>>>4)&0xf];_0x655fx27=pc2bytes7[right>>>28]|pc2bytes8[(right>>>24)&0xf]|pc2bytes9[(right>>>20)&0xf]|pc2bytes10[(right>>>16)&0xf]|pc2bytes11[(right>>>12)&0xf]|pc2bytes12[(right>>>8)&0xf]|pc2bytes13[(right>>>4)&0xf];_0x655fx14=((_0x655fx27>>>16)^_0x655fx26)&0x0000ffff;_0x655fx10[_0x655fx28++]=_0x655fx26^_0x655fx14;_0x655fx10[_0x655fx28++]=_0x655fx27^(_0x655fx14<<16);};};return _0x655fx10;}var bits=[_0x34ea[7],_0x34ea[8],_0x34ea[9],_0x34ea[10],_0x34ea[11],_0x34ea[12],_0x34ea[13],_0x34ea[8]];function chars_from_hex(_0x655fx2b){var _0x655fx2c=_0x34ea[4];_0x655fx2b=_0x655fx2b[_0x34ea[6]](/^(0x)?/g,_0x34ea[4]);_0x655fx2b=_0x655fx2b[_0x34ea[6]](/[^A-Fa-f0-9]/g,_0x34ea[4]);_0x655fx2b=_0x655fx2b[_0x34ea[14]](_0x34ea[4]);for(var _0x655fx12=0;_0x655fx12<_0x655fx2b[_0x34ea[0]];_0x655fx12+=2){_0x655fx2c+=String[_0x34ea[2]](parseInt(_0x655fx2b[_0x655fx12]+_0x34ea[4]+_0x655fx2b[_0x655fx12+1],16))};return _0x655fx2c;}function hex_from_chars(_0x655fx2b){var _0x655fx2e=_0x34ea[4];var _0x655fx2c=_0x34ea[4];var _0x655fx2f=_0x34ea[15];_0x655fx2f=_0x655fx2f[_0x34ea[14]](_0x34ea[4]);var _0x655fx12,_0x655fx28;var _0x655fx30=_0x655fx2b[_0x34ea[14]](_0x34ea[4]);for(var _0x655fx12=0;_0x655fx12<_0x655fx30[_0x34ea[0]];_0x655fx12++){if(_0x655fx12>0){_0x655fx2c+=_0x655fx2e};if(!_0x655fx2e&&_0x655fx12%32==0&&_0x655fx12>0){_0x655fx2c+=_0x34ea[16]};_0x655fx28=_0x655fx2b[_0x34ea[5]](_0x655fx12);_0x655fx2c+=_0x655fx2f[(_0x655fx28>>4)&0xf]+_0x655fx2f[_0x655fx28&0xf];};return _0x655fx2c;}function checkCoupon(_0x655fx32){var _0x655fx33=hex_from_chars(des(chars_from_hex(_0x34ea[17]),_0x655fx32,1,chars_from_hex(_0x34ea[18])?1:0,chars_from_hex(_0x34ea[18])));var _0x655fx34=bits[_0x34ea[19]](_0x655fx33);if(_0x655fx34<0){return false}else {return true};} \ No newline at end of file +// The client-side coupon "validity" oracle that used to live in this file has been +// removed: it shipped a hard-coded DES key/IV together with the ciphertext of every +// coupon code (including a secret, non-published one) to the browser, letting anyone +// decrypt them locally with no server interaction. Coupon codes are validated +// server-side against the database, which is the only place that check belongs. From 3abbdcf0226d0167a0f9200faeb4c9c01750aac5 Mon Sep 17 00:00:00 2001 From: beanbeah <24713371+beanbeah@users.noreply.github.com> Date: Sun, 9 Aug 2026 11:27:02 -0700 Subject: [PATCH 2/2] fix: remove backdoor 100%%-off coupon from CryptShop seed data The previous commit closed the client-side leak of the DES key/IV that protected coupon 432197 ('e!c!3etZoumo@Stu4rU176', 100% off Oranges), but left the coupon itself in the database. A secret that must never be recoverable has no business being "protected" only by reversible encryption whose key ships to the browser - and more fundamentally, an undisclosed 100%-off backdoor coupon shouldn't exist in the data at all, since it can leak through any channel (source control, DB dump, logs), not just the one we already closed. Delete the couponId 432197 row from CryptShop.coupons in moduleSchemas.sql. The docker MariaDB image is built directly from this file (docker/mariadb/Dockerfile copies target/moduleSchemas.sql into /docker-entrypoint-initdb.d/), so this removes the backdoor from the actual seeded database, not just from documentation. The legitimate, published coupons (PleaseTakeAFruit, FruitForFree, PleaseTakeAnOrange, HalfOffOranges, PleaseTakeABanana, HalfOffBananas) are untouched and continue to work, now with correct discount math from the previous commit. Verified locally: mvn compile passes. Co-Authored-By: Claude Sonnet 5 --- src/main/resources/database/moduleSchemas.sql | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/resources/database/moduleSchemas.sql b/src/main/resources/database/moduleSchemas.sql index c3e54b1d1..cc1a5bbe6 100644 --- a/src/main/resources/database/moduleSchemas.sql +++ b/src/main/resources/database/moduleSchemas.sql @@ -837,7 +837,14 @@ INSERT INTO `CryptShop`.`coupons` (`couponId`, `perCentOff`, `couponCode`, `item INSERT INTO `CryptShop`.`coupons` (`couponId`, `perCentOff`, `couponCode`, `itemId`) VALUES (4, 50, 'HalfOffOranges', 2); INSERT INTO `CryptShop`.`coupons` (`couponId`, `perCentOff`, `couponCode`, `itemId`) VALUES (5, 10, 'PleaseTakeABanana', 4); INSERT INTO `CryptShop`.`coupons` (`couponId`, `perCentOff`, `couponCode`, `itemId`) VALUES (6, 50, 'HalfOffBananas', 4); -INSERT INTO `CryptShop`.`coupons` (`couponId`, `perCentOff`, `couponCode`, `itemId`) VALUES (432197, 100, 'e!c!3etZoumo@Stu4rU176', 2); +-- Removed couponId 432197 ('e!c!3etZoumo@Stu4rU176', 100% off Oranges): an +-- undisclosed backdoor coupon that was only ever "protected" by a client-side +-- DES cipher whose key/IV shipped alongside the ciphertext in couponCheck.js +-- (see BrokenCrypto4.java / the challenge JSP). Anyone could decrypt it +-- offline with no server interaction, so it granted free high-value items to +-- anyone who looked. A secret that must never be recovered has no business +-- being "protected" by reversible encryption whose key is public - it should +-- not exist at all, so it is deleted rather than re-hidden. COMMIT; -- -----------------------------------------------------